pynintendoparental 0.6.8__tar.gz → 0.7.0__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.
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/PKG-INFO +1 -1
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/__init__.py +1 -4
- pynintendoparental-0.7.0/pynintendoparental/_version.py +1 -0
- pynintendoparental-0.7.0/pynintendoparental/api.py +222 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/device.py +66 -41
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental.egg-info/PKG-INFO +1 -1
- pynintendoparental-0.6.8/pynintendoparental/_version.py +0 -1
- pynintendoparental-0.6.8/pynintendoparental/api.py +0 -96
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/LICENSE +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/README.md +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/application.py +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/authenticator/__init__.py +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/authenticator/const.py +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/const.py +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/enum.py +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/exceptions.py +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/player.py +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/py.typed +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/utils.py +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental.egg-info/SOURCES.txt +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental.egg-info/dependency_links.txt +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental.egg-info/requires.txt +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental.egg-info/top_level.txt +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pyproject.toml +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/setup.cfg +0 -0
- {pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/setup.py +0 -0
|
@@ -30,10 +30,7 @@ class NintendoParental:
|
|
|
30
30
|
dev.device_id,
|
|
31
31
|
err)
|
|
32
32
|
|
|
33
|
-
response = await self._api.
|
|
34
|
-
endpoint="get_account_devices",
|
|
35
|
-
ACCOUNT_ID=self.account_id
|
|
36
|
-
)
|
|
33
|
+
response = await self._api.async_get_account_devices()
|
|
37
34
|
|
|
38
35
|
for dev_raw in response["json"]["items"]:
|
|
39
36
|
device: Device = Device.from_device_response(dev_raw, self._api)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.7.0"
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""API handler."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
|
+
|
|
5
|
+
import aiohttp
|
|
6
|
+
|
|
7
|
+
from .authenticator import Authenticator
|
|
8
|
+
from .const import (
|
|
9
|
+
ENDPOINTS,
|
|
10
|
+
BASE_URL,
|
|
11
|
+
USER_AGENT,
|
|
12
|
+
MOBILE_APP_PKG,
|
|
13
|
+
MOBILE_APP_BUILD,
|
|
14
|
+
MOBILE_APP_VERSION,
|
|
15
|
+
OS_VERSION,
|
|
16
|
+
OS_NAME,
|
|
17
|
+
DEVICE_MODEL,
|
|
18
|
+
_LOGGER
|
|
19
|
+
)
|
|
20
|
+
from .exceptions import HttpException
|
|
21
|
+
|
|
22
|
+
def _check_http_success(status: int) -> bool:
|
|
23
|
+
return status >= 200 and status < 300
|
|
24
|
+
|
|
25
|
+
class Api:
|
|
26
|
+
"""Nintendo Parental Controls API."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, auth, tz, lang, session: aiohttp.ClientSession=None):
|
|
29
|
+
"""INIT"""
|
|
30
|
+
self._auth: Authenticator = auth
|
|
31
|
+
self._tz = tz
|
|
32
|
+
self._language = lang
|
|
33
|
+
if session is None:
|
|
34
|
+
session = aiohttp.ClientSession()
|
|
35
|
+
self._session_created_internally = True
|
|
36
|
+
self._session = session
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def _auth_token(self) -> str:
|
|
40
|
+
"""Returns the auth token."""
|
|
41
|
+
return f"Bearer {self._auth.access_token}"
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def account_id(self):
|
|
45
|
+
"""Return the account id."""
|
|
46
|
+
return self._auth.account_id
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def _headers(self) -> dict:
|
|
50
|
+
"""Return web request headers."""
|
|
51
|
+
return {
|
|
52
|
+
"User-Agent": USER_AGENT,
|
|
53
|
+
"X-Moon-App-Id": MOBILE_APP_PKG,
|
|
54
|
+
"X-Moon-Os": OS_NAME,
|
|
55
|
+
"X-Moon-Os-Version": OS_VERSION,
|
|
56
|
+
"X-Moon-Model": DEVICE_MODEL,
|
|
57
|
+
"X-Moon-App-Display-Version": MOBILE_APP_VERSION,
|
|
58
|
+
"X-Moon-App-Internal-Version": MOBILE_APP_BUILD,
|
|
59
|
+
"X-Moon-TimeZone": self._tz,
|
|
60
|
+
"X-Moon-Os-Language": self._language,
|
|
61
|
+
"X-Moon-App-Language": self._language,
|
|
62
|
+
"Authorization": self._auth_token
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async def async_close(self):
|
|
66
|
+
"""Closes the underlying aiohttp.ClientSession if it was created by this instance."""
|
|
67
|
+
if hasattr(self, '_session_created_internally') and self._session_created_internally and self._session and not self._session.closed:
|
|
68
|
+
await self._session.close()
|
|
69
|
+
self._session = None # Optional: clear the session attribute
|
|
70
|
+
|
|
71
|
+
async def send_request(self, endpoint: str, body: object=None, **kwargs):
|
|
72
|
+
"""Sends a request to a given endpoint."""
|
|
73
|
+
_LOGGER.debug("Sending request to %s", endpoint)
|
|
74
|
+
# Get the endpoint from the endpoints map
|
|
75
|
+
e_point = ENDPOINTS.get(endpoint, None)
|
|
76
|
+
if e_point is None:
|
|
77
|
+
raise ValueError("Endpoint does not exist")
|
|
78
|
+
# refresh the token if it has expired.
|
|
79
|
+
if self._auth.expires < (datetime.now()+timedelta(seconds=30)):
|
|
80
|
+
_LOGGER.debug("Access token expired, requesting refresh.")
|
|
81
|
+
await self._auth.perform_refresh()
|
|
82
|
+
# format the URL using the kwargs
|
|
83
|
+
url = e_point.get("url").format(BASE_URL=BASE_URL, **kwargs)
|
|
84
|
+
_LOGGER.debug("Built URL %s", url)
|
|
85
|
+
# now send the HTTP request
|
|
86
|
+
resp: dict = {
|
|
87
|
+
"status": 0,
|
|
88
|
+
"text": "",
|
|
89
|
+
"json": "",
|
|
90
|
+
"headers": ""
|
|
91
|
+
}
|
|
92
|
+
self._session.headers.update(self._headers)
|
|
93
|
+
async with self._session.request(
|
|
94
|
+
method=e_point.get("method"),
|
|
95
|
+
url=url,
|
|
96
|
+
json=body
|
|
97
|
+
) as response:
|
|
98
|
+
_LOGGER.debug("%s request to %s status code %s",
|
|
99
|
+
e_point.get("method"),
|
|
100
|
+
url,
|
|
101
|
+
response.status)
|
|
102
|
+
if _check_http_success(response.status):
|
|
103
|
+
resp["status"] = response.status
|
|
104
|
+
resp["text"] = await response.text()
|
|
105
|
+
try:
|
|
106
|
+
resp["json"] = await response.json()
|
|
107
|
+
except (aiohttp.ContentTypeError, ValueError) as e:
|
|
108
|
+
_LOGGER.warning(
|
|
109
|
+
"""Failed to decode JSON response from %s.
|
|
110
|
+
Status: %s, Error: %s.
|
|
111
|
+
Response text: %s...""",
|
|
112
|
+
url, response.status, e, resp['text'][:200]
|
|
113
|
+
)
|
|
114
|
+
resp["json"] = {}
|
|
115
|
+
resp["headers"] = response.headers
|
|
116
|
+
else:
|
|
117
|
+
raise HttpException("HTTP Error", response.status, await response.text())
|
|
118
|
+
|
|
119
|
+
# now return the resp dict
|
|
120
|
+
return resp
|
|
121
|
+
|
|
122
|
+
async def async_get_account_details(self) -> dict:
|
|
123
|
+
"""Get account details."""
|
|
124
|
+
return await self.send_request(
|
|
125
|
+
endpoint="get_account_details",
|
|
126
|
+
ACCOUNT_ID=self.account_id
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
async def async_get_account_devices(self) -> dict:
|
|
130
|
+
"""Get account devices."""
|
|
131
|
+
return await self.send_request(
|
|
132
|
+
endpoint="get_account_devices",
|
|
133
|
+
ACCOUNT_ID=self.account_id
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
async def async_get_account_device(self, device_id: str) -> dict:
|
|
137
|
+
"""Get account device."""
|
|
138
|
+
return await self.send_request(
|
|
139
|
+
endpoint="get_account_device",
|
|
140
|
+
ACCOUNT_ID=self.account_id,
|
|
141
|
+
DEVICE_ID=device_id
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
async def async_get_device_daily_summaries(self, device_id: str) -> dict:
|
|
145
|
+
"""Get device daily summaries."""
|
|
146
|
+
return await self.send_request(
|
|
147
|
+
endpoint="get_device_daily_summaries",
|
|
148
|
+
DEVICE_ID=device_id
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
async def async_get_device_monthly_summaries(self, device_id: str) -> dict:
|
|
152
|
+
"""Get device monthly summaries."""
|
|
153
|
+
return await self.send_request(
|
|
154
|
+
endpoint="get_device_monthly_summaries",
|
|
155
|
+
DEVICE_ID=device_id
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
async def async_get_device_parental_control_setting(self, device_id: str) -> dict:
|
|
159
|
+
"""Get device parental control setting."""
|
|
160
|
+
return await self.send_request(
|
|
161
|
+
endpoint="get_device_parental_control_setting",
|
|
162
|
+
DEVICE_ID=device_id
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
async def async_get_device_parental_control_setting_state(self, device_id: str) -> dict:
|
|
166
|
+
"""Get device parental control setting state."""
|
|
167
|
+
return await self.send_request(
|
|
168
|
+
endpoint="get_device_parental_control_setting_state",
|
|
169
|
+
DEVICE_ID=device_id
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
async def async_get_device_alarm_setting_state(self, device_id: str) -> dict:
|
|
173
|
+
"""Get device alarm setting state."""
|
|
174
|
+
return await self.send_request(
|
|
175
|
+
endpoint="get_device_alarm_setting_state",
|
|
176
|
+
DEVICE_ID=device_id
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
async def async_get_device_monthly_summary(self, device_id: str, year: int, month: int) -> dict:
|
|
180
|
+
"""Get device monthly summary."""
|
|
181
|
+
return await self.send_request(
|
|
182
|
+
endpoint="get_device_monthly_summary",
|
|
183
|
+
DEVICE_ID=device_id,
|
|
184
|
+
YEAR=year,
|
|
185
|
+
MONTH=f"{month:02d}"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
async def async_set_device_parental_control_setting(
|
|
189
|
+
self,
|
|
190
|
+
device_id: str,
|
|
191
|
+
settings: dict
|
|
192
|
+
) -> dict:
|
|
193
|
+
"""Update device parental control setting."""
|
|
194
|
+
return await self.send_request(
|
|
195
|
+
endpoint="update_device_parental_control_setting",
|
|
196
|
+
DEVICE_ID=device_id,
|
|
197
|
+
body=settings
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
async def async_set_device_whitelisted_applications(
|
|
201
|
+
self,
|
|
202
|
+
device_id: str,
|
|
203
|
+
applications: dict
|
|
204
|
+
) -> dict:
|
|
205
|
+
"""Update device whitelisted applications."""
|
|
206
|
+
return await self.send_request(
|
|
207
|
+
endpoint="update_device_whitelisted_applications",
|
|
208
|
+
DEVICE_ID=device_id,
|
|
209
|
+
body=applications
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
async def async_set_device_alarm_setting_state(
|
|
213
|
+
self,
|
|
214
|
+
device_id: str,
|
|
215
|
+
alarm_state: dict
|
|
216
|
+
) -> dict:
|
|
217
|
+
"""Update device alarm setting state."""
|
|
218
|
+
return await self.send_request(
|
|
219
|
+
endpoint="update_device_alarm_setting_state",
|
|
220
|
+
DEVICE_ID=device_id,
|
|
221
|
+
body=alarm_state
|
|
222
|
+
)
|
|
@@ -27,13 +27,14 @@ class Device:
|
|
|
27
27
|
self.daily_summaries: dict = {}
|
|
28
28
|
self.parental_control_settings: dict = {}
|
|
29
29
|
self.players: list[Player] = []
|
|
30
|
-
self.limit_time: int = 0
|
|
30
|
+
self.limit_time: int | float | None = 0
|
|
31
31
|
self.timer_mode: str = ""
|
|
32
|
-
self.today_playing_time: int = 0
|
|
33
|
-
self.
|
|
34
|
-
self.
|
|
35
|
-
self.
|
|
36
|
-
self.
|
|
32
|
+
self.today_playing_time: int | float = 0
|
|
33
|
+
self.today_time_remaining: int | float = 0
|
|
34
|
+
self.bedtime_alarm: time | None = None
|
|
35
|
+
self.month_playing_time: int | float = 0
|
|
36
|
+
self.today_disabled_time: int | float = 0
|
|
37
|
+
self.today_exceeded_time: int | float = 0
|
|
37
38
|
self.today_notices: list = []
|
|
38
39
|
self.today_important_info: list = []
|
|
39
40
|
self.today_observations: list = []
|
|
@@ -114,10 +115,9 @@ class Device:
|
|
|
114
115
|
async def _set_parental_control_setting(self):
|
|
115
116
|
"""Shortcut method to deduplicate code used to update parental control settings."""
|
|
116
117
|
_LOGGER.debug(">> Device._set_parental_control_setting()")
|
|
117
|
-
await self._api.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
DEVICE_ID=self.device_id
|
|
118
|
+
await self._api.async_set_device_parental_control_setting(
|
|
119
|
+
settings=self._get_update_parental_control_setting_body(),
|
|
120
|
+
device_id=self.device_id
|
|
121
121
|
)
|
|
122
122
|
await self._get_parental_control_setting()
|
|
123
123
|
|
|
@@ -183,9 +183,10 @@ class Device:
|
|
|
183
183
|
current_day = day_of_week_regs.get(DAYS_OF_WEEK[datetime.now().weekday()], {})
|
|
184
184
|
self.timer_mode = self.parental_control_settings["playTimerRegulations"]["timerMode"]
|
|
185
185
|
if self.timer_mode == "EACH_DAY_OF_THE_WEEK":
|
|
186
|
-
self.limit_time = current_day
|
|
186
|
+
self.limit_time = current_day.get("timeToPlayInOneDay", {}).get("limitTime", None)
|
|
187
187
|
else:
|
|
188
|
-
self.limit_time = self.parental_control_settings
|
|
188
|
+
self.limit_time = self.parental_control_settings.get("playTimerRegulations", {}).get(
|
|
189
|
+
"dailyRegulations", {}).get("timeToPlayInOneDay", {}).get("limitTime", None)
|
|
189
190
|
|
|
190
191
|
if self.timer_mode == "EACH_DAY_OF_THE_WEEK":
|
|
191
192
|
if current_day["bedtime"]["enabled"]:
|
|
@@ -207,9 +208,8 @@ class Device:
|
|
|
207
208
|
async def _get_parental_control_setting(self):
|
|
208
209
|
"""Retreives parental control settings from the API."""
|
|
209
210
|
_LOGGER.debug(">> Device._get_parental_control_setting()")
|
|
210
|
-
response = await self._api.
|
|
211
|
-
|
|
212
|
-
DEVICE_ID=self.device_id
|
|
211
|
+
response = await self._api.async_get_device_parental_control_setting(
|
|
212
|
+
device_id=self.device_id
|
|
213
213
|
)
|
|
214
214
|
self.parental_control_settings = response["json"]
|
|
215
215
|
if "bedtimeStartingTime" in self.parental_control_settings["playTimerRegulations"]:
|
|
@@ -233,22 +233,53 @@ class Device:
|
|
|
233
233
|
async def _get_daily_summaries(self):
|
|
234
234
|
"""Retrieve daily summaries."""
|
|
235
235
|
_LOGGER.debug(">> Device._get_daily_summaries()")
|
|
236
|
-
response = await self._api.
|
|
237
|
-
|
|
238
|
-
DEVICE_ID = self.device_id
|
|
236
|
+
response = await self._api.async_get_device_daily_summaries(
|
|
237
|
+
device_id = self.device_id
|
|
239
238
|
)
|
|
240
239
|
self.daily_summaries = response["json"]["items"]
|
|
241
240
|
_LOGGER.debug("New daily summary %s", self.daily_summaries)
|
|
242
241
|
try:
|
|
243
242
|
today_playing_time = self.get_date_summary()[0].get("playingTime", 0)
|
|
244
|
-
self.today_playing_time =
|
|
243
|
+
self.today_playing_time = 0 if today_playing_time is None else today_playing_time/60
|
|
245
244
|
today_disabled_time = self.get_date_summary()[0].get("disabledTime", 0)
|
|
246
|
-
self.today_disabled_time =
|
|
245
|
+
self.today_disabled_time = 0 if today_disabled_time is None else today_disabled_time/60
|
|
247
246
|
today_exceeded_time = self.get_date_summary()[0].get("exceededTime", 0)
|
|
248
|
-
self.today_exceeded_time =
|
|
247
|
+
self.today_exceeded_time = 0 if today_exceeded_time is None else today_exceeded_time/60
|
|
249
248
|
_LOGGER.debug("Cached playing, disabled and exceeded time for today for device %s",
|
|
250
249
|
self.device_id)
|
|
250
|
+
now = datetime.now()
|
|
251
|
+
current_minutes_past_midnight = now.hour * 60 + now.minute
|
|
252
|
+
minutes_in_day = 1440 # 24 * 60
|
|
253
|
+
|
|
254
|
+
# 1. Calculate remaining time based on play limit
|
|
255
|
+
|
|
256
|
+
time_remaining_by_play_limit = 0.0
|
|
257
|
+
if self.limit_time is None:
|
|
258
|
+
# No specific play limit, effectively limited by end of day for this calculation step.
|
|
259
|
+
time_remaining_by_play_limit = float(minutes_in_day - current_minutes_past_midnight)
|
|
260
|
+
elif self.limit_time == 0:
|
|
261
|
+
time_remaining_by_play_limit = 0.0
|
|
262
|
+
else:
|
|
263
|
+
time_remaining_by_play_limit = float(self.limit_time - self.today_playing_time)
|
|
264
|
+
|
|
265
|
+
time_remaining_by_play_limit = max(0.0, time_remaining_by_play_limit)
|
|
266
|
+
|
|
267
|
+
# Initialize overall remaining time with play limit constraint
|
|
268
|
+
effective_remaining_time = time_remaining_by_play_limit
|
|
269
|
+
|
|
270
|
+
# 2. Factor in bedtime alarm, if any, to further constrain remaining time
|
|
271
|
+
if self.bedtime_alarm is not None:
|
|
272
|
+
bedtime_dt = datetime.combine(now.date(), self.bedtime_alarm)
|
|
273
|
+
time_remaining_by_bedtime = 0.0
|
|
274
|
+
if bedtime_dt > now: # Bedtime is in the future today
|
|
275
|
+
time_remaining_by_bedtime = (bedtime_dt - now).total_seconds() / 60
|
|
276
|
+
time_remaining_by_bedtime = max(0.0, time_remaining_by_bedtime)
|
|
277
|
+
# else: Bedtime has passed for today or is now, so time_remaining_by_bedtime remains 0.0
|
|
278
|
+
|
|
279
|
+
effective_remaining_time = min(effective_remaining_time, time_remaining_by_bedtime)
|
|
251
280
|
|
|
281
|
+
self.today_time_remaining = int(max(0.0, effective_remaining_time)) # Ensure non-negative and integer
|
|
282
|
+
_LOGGER.debug("Calculated and updated the amount of time remaining for today: %s", self.today_time_remaining)
|
|
252
283
|
self.today_important_info = self.get_date_summary()[0].get("importantInfos", [])
|
|
253
284
|
self.today_notices = self.get_date_summary()[0].get("notices", [])
|
|
254
285
|
self.today_observations = self.get_date_summary()[0].get("observations", [])
|
|
@@ -300,10 +331,8 @@ class Device:
|
|
|
300
331
|
_LOGGER.debug(">> Device._get_extras()")
|
|
301
332
|
if self.alarms_enabled is not None:
|
|
302
333
|
# first refresh can come from self.extra without http request
|
|
303
|
-
response = await self._api.
|
|
304
|
-
|
|
305
|
-
ACCOUNT_ID = self._api.account_id,
|
|
306
|
-
DEVICE_ID = self.device_id
|
|
334
|
+
response = await self._api.async_get_account_device(
|
|
335
|
+
device_id = self.device_id
|
|
307
336
|
)
|
|
308
337
|
self.extra = response["json"]
|
|
309
338
|
status = self.extra["device"]["alarmSetting"]["visibility"]
|
|
@@ -317,9 +346,8 @@ class Device:
|
|
|
317
346
|
_LOGGER.debug(">> Device.get_monthly_summary(search_date=%s)", search_date)
|
|
318
347
|
latest = False
|
|
319
348
|
if search_date is None:
|
|
320
|
-
response = await self._api.
|
|
321
|
-
|
|
322
|
-
DEVICE_ID=self.device_id
|
|
349
|
+
response = await self._api.async_get_device_monthly_summaries(
|
|
350
|
+
device_id=self.device_id
|
|
323
351
|
)
|
|
324
352
|
_LOGGER.debug("Available monthly summaries: %s", response)
|
|
325
353
|
response = response["json"]["indexes"][0]
|
|
@@ -328,11 +356,10 @@ class Device:
|
|
|
328
356
|
latest = True
|
|
329
357
|
|
|
330
358
|
try:
|
|
331
|
-
response = await self._api.
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
MONTH=str(search_date.month).zfill(2)
|
|
359
|
+
response = await self._api.async_get_device_monthly_summary(
|
|
360
|
+
device_id = self.device_id,
|
|
361
|
+
year=search_date.year,
|
|
362
|
+
month=search_date.month
|
|
336
363
|
)
|
|
337
364
|
_LOGGER.debug("Monthly summary query complete for device %s: %s",
|
|
338
365
|
self.device_id,
|
|
@@ -351,12 +378,11 @@ class Device:
|
|
|
351
378
|
"""Updates the alarm state for the device."""
|
|
352
379
|
_LOGGER.debug(">> Device.set_alarm_state(state=%s)",
|
|
353
380
|
state)
|
|
354
|
-
await self._api.
|
|
355
|
-
|
|
356
|
-
body={
|
|
381
|
+
await self._api.async_set_device_alarm_setting_state(
|
|
382
|
+
alarm_state={
|
|
357
383
|
"status": str(state)
|
|
358
384
|
},
|
|
359
|
-
|
|
385
|
+
device_id = self.device_id
|
|
360
386
|
)
|
|
361
387
|
|
|
362
388
|
async def set_whitelisted_application(self, app_id: str, allowed: bool):
|
|
@@ -369,10 +395,9 @@ class Device:
|
|
|
369
395
|
# take a snapshot of the whitelisted apps state
|
|
370
396
|
current_state = self.parental_control_settings["whitelistedApplications"]
|
|
371
397
|
current_state[app_id]["safeLaunch"] = "ALLOW" if allowed else "NONE"
|
|
372
|
-
await self._api.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
DEVICE_ID=self.device_id
|
|
398
|
+
await self._api.async_set_device_whitelisted_applications(
|
|
399
|
+
applications=current_state,
|
|
400
|
+
device_id=self.device_id
|
|
376
401
|
)
|
|
377
402
|
await self._get_parental_control_setting()
|
|
378
403
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.6.8"
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
"""API handler."""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime, timedelta
|
|
4
|
-
|
|
5
|
-
import aiohttp
|
|
6
|
-
|
|
7
|
-
from .authenticator import Authenticator
|
|
8
|
-
from .const import (
|
|
9
|
-
ENDPOINTS,
|
|
10
|
-
BASE_URL,
|
|
11
|
-
USER_AGENT,
|
|
12
|
-
MOBILE_APP_PKG,
|
|
13
|
-
MOBILE_APP_BUILD,
|
|
14
|
-
MOBILE_APP_VERSION,
|
|
15
|
-
OS_VERSION,
|
|
16
|
-
OS_NAME,
|
|
17
|
-
DEVICE_MODEL,
|
|
18
|
-
_LOGGER
|
|
19
|
-
)
|
|
20
|
-
from .exceptions import HttpException
|
|
21
|
-
|
|
22
|
-
def _check_http_success(status: int) -> bool:
|
|
23
|
-
return status >= 200 and status < 300
|
|
24
|
-
|
|
25
|
-
class Api:
|
|
26
|
-
"""Nintendo Parental Controls API."""
|
|
27
|
-
|
|
28
|
-
def __init__(self, auth, tz, lang):
|
|
29
|
-
"""INIT"""
|
|
30
|
-
self._auth: Authenticator = auth
|
|
31
|
-
self._tz = tz
|
|
32
|
-
self._language = lang
|
|
33
|
-
|
|
34
|
-
@property
|
|
35
|
-
def _auth_token(self) -> str:
|
|
36
|
-
"""Returns the auth token."""
|
|
37
|
-
return f"Bearer {self._auth.access_token}"
|
|
38
|
-
|
|
39
|
-
@property
|
|
40
|
-
def account_id(self):
|
|
41
|
-
"""Return the account id."""
|
|
42
|
-
return self._auth.account_id
|
|
43
|
-
|
|
44
|
-
async def send_request(self, endpoint: str, body: object=None, **kwargs):
|
|
45
|
-
"""Sends a request to a given endpoint."""
|
|
46
|
-
_LOGGER.debug("Sending request to %s", endpoint)
|
|
47
|
-
# Get the endpoint from the endpoints map
|
|
48
|
-
e_point = ENDPOINTS.get(endpoint, None)
|
|
49
|
-
if e_point is None:
|
|
50
|
-
raise ValueError("Endpoint does not exist")
|
|
51
|
-
# refresh the token if it has expired.
|
|
52
|
-
if self._auth.expires < (datetime.now()+timedelta(seconds=30)):
|
|
53
|
-
_LOGGER.debug("Access token expired, requesting refresh.")
|
|
54
|
-
await self._auth.perform_refresh()
|
|
55
|
-
# format the URL using the kwargs
|
|
56
|
-
url = e_point.get("url").format(BASE_URL=BASE_URL, **kwargs)
|
|
57
|
-
_LOGGER.debug("Built URL %s", url)
|
|
58
|
-
# now send the HTTP request
|
|
59
|
-
resp: dict = {
|
|
60
|
-
"status": 0,
|
|
61
|
-
"text": "",
|
|
62
|
-
"json": "",
|
|
63
|
-
"headers": ""
|
|
64
|
-
}
|
|
65
|
-
async with aiohttp.ClientSession() as session:
|
|
66
|
-
# Add auth header
|
|
67
|
-
session.headers.add("Authorization", self._auth_token)
|
|
68
|
-
session.headers.add("User-Agent", USER_AGENT)
|
|
69
|
-
session.headers.add("X-Moon-App-Id", MOBILE_APP_PKG)
|
|
70
|
-
session.headers.add("X-Moon-Os", OS_NAME)
|
|
71
|
-
session.headers.add("X-Moon-Os-Version", OS_VERSION)
|
|
72
|
-
session.headers.add("X-Moon-Model", DEVICE_MODEL)
|
|
73
|
-
session.headers.add("X-Moon-TimeZone", self._tz)
|
|
74
|
-
session.headers.add("X-Moon-Os-Language", self._language)
|
|
75
|
-
session.headers.add("X-Moon-App-Language", self._language)
|
|
76
|
-
session.headers.add("X-Moon-App-Display-Version", MOBILE_APP_VERSION)
|
|
77
|
-
session.headers.add("X-Moon-App-Internal-Version", MOBILE_APP_BUILD)
|
|
78
|
-
async with session.request(
|
|
79
|
-
method=e_point.get("method"),
|
|
80
|
-
url=url,
|
|
81
|
-
json=body
|
|
82
|
-
) as response:
|
|
83
|
-
_LOGGER.debug("%s request to %s status code %s",
|
|
84
|
-
e_point.get("method"),
|
|
85
|
-
url,
|
|
86
|
-
response.status)
|
|
87
|
-
if _check_http_success(response.status):
|
|
88
|
-
resp["status"] = response.status
|
|
89
|
-
resp["text"] = await response.text()
|
|
90
|
-
resp["json"] = await response.json()
|
|
91
|
-
resp["headers"] = response.headers
|
|
92
|
-
else:
|
|
93
|
-
raise HttpException("HTTP Error", response.status, await response.text())
|
|
94
|
-
|
|
95
|
-
# now return the resp dict
|
|
96
|
-
return resp
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/authenticator/__init__.py
RENAMED
|
File without changes
|
{pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental/authenticator/const.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental.egg-info/requires.txt
RENAMED
|
File without changes
|
{pynintendoparental-0.6.8 → pynintendoparental-0.7.0}/pynintendoparental.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|