Skip to content

Commit 162737a

Browse files
authored
Add actions for Nintendo Parental Controls (#154886)
1 parent d074c5b commit 162737a

File tree

9 files changed

+199
-8
lines changed

9 files changed

+199
-8
lines changed

homeassistant/components/nintendo_parental_controls/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
from homeassistant.const import Platform
1212
from homeassistant.core import HomeAssistant
1313
from homeassistant.exceptions import ConfigEntryAuthFailed
14+
from homeassistant.helpers import config_validation as cv
1415
from homeassistant.helpers.aiohttp_client import async_get_clientsession
16+
from homeassistant.helpers.typing import ConfigType
1517

1618
from .const import CONF_SESSION_TOKEN, DOMAIN
1719
from .coordinator import NintendoParentalControlsConfigEntry, NintendoUpdateCoordinator
20+
from .services import async_setup_services
1821

1922
_PLATFORMS: list[Platform] = [
2023
Platform.SENSOR,
@@ -23,6 +26,14 @@
2326
Platform.NUMBER,
2427
]
2528

29+
PLATFORM_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
30+
31+
32+
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
33+
"""Set up the Nintendo Switch Parental Controls integration."""
34+
async_setup_services(hass)
35+
return True
36+
2637

2738
async def async_setup_entry(
2839
hass: HomeAssistant, entry: NintendoParentalControlsConfigEntry

homeassistant/components/nintendo_parental_controls/const.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@
77
BEDTIME_ALARM_MIN = "16:00"
88
BEDTIME_ALARM_MAX = "23:00"
99
BEDTIME_ALARM_DISABLE = "00:00"
10+
11+
ATTR_BONUS_TIME = "bonus_time"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"services": {
3+
"add_bonus_time": {
4+
"service": "mdi:timer-plus-outline"
5+
}
6+
}
7+
}

homeassistant/components/nintendo_parental_controls/quality_scale.yaml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
rules:
22
# Bronze
3-
action-setup:
4-
status: exempt
5-
comment: |
6-
No custom actions are defined.
3+
action-setup: done
74
appropriate-polling: done
85
brands: done
96
common-modules: done
107
config-flow-test-coverage: done
118
config-flow: done
129
dependency-transparency: done
13-
docs-actions:
14-
status: exempt
15-
comment: |
16-
No custom actions are defined.
10+
docs-actions: done
1711
docs-high-level-description: done
1812
docs-installation-instructions: done
1913
docs-removal-instructions: done
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""Services for Nintendo Parental integration."""
2+
3+
from enum import StrEnum
4+
import logging
5+
6+
import voluptuous as vol
7+
8+
from homeassistant.const import ATTR_DEVICE_ID
9+
from homeassistant.core import HomeAssistant, ServiceCall, callback
10+
from homeassistant.exceptions import HomeAssistantError
11+
from homeassistant.helpers import config_validation as cv, device_registry as dr
12+
13+
from .const import ATTR_BONUS_TIME, DOMAIN
14+
from .coordinator import NintendoParentalControlsConfigEntry
15+
16+
_LOGGER = logging.getLogger(__name__)
17+
18+
19+
class NintendoParentalServices(StrEnum):
20+
"""Store keys for Nintendo Parental services."""
21+
22+
ADD_BONUS_TIME = "add_bonus_time"
23+
24+
25+
@callback
26+
def async_setup_services(
27+
hass: HomeAssistant,
28+
):
29+
"""Set up the Nintendo Parental services."""
30+
hass.services.async_register(
31+
domain=DOMAIN,
32+
service=NintendoParentalServices.ADD_BONUS_TIME,
33+
service_func=async_add_bonus_time,
34+
schema=vol.Schema(
35+
{
36+
vol.Required(ATTR_DEVICE_ID): cv.string,
37+
vol.Required(ATTR_BONUS_TIME): vol.All(int, vol.Range(min=5, max=30)),
38+
}
39+
),
40+
)
41+
42+
43+
def _get_nintendo_device_id(dev: dr.DeviceEntry) -> str | None:
44+
"""Get the Nintendo device ID from a device entry."""
45+
for identifier in dev.identifiers:
46+
if identifier[0] == DOMAIN:
47+
return identifier[1].split("_")[-1]
48+
return None
49+
50+
51+
async def async_add_bonus_time(call: ServiceCall) -> None:
52+
"""Add bonus time to a device."""
53+
config_entry: NintendoParentalControlsConfigEntry | None
54+
data = call.data
55+
device_id: str = data[ATTR_DEVICE_ID]
56+
bonus_time: int = data[ATTR_BONUS_TIME]
57+
device = dr.async_get(call.hass).async_get(device_id)
58+
if device is None:
59+
raise HomeAssistantError(
60+
translation_domain=DOMAIN,
61+
translation_key="device_not_found",
62+
)
63+
for entry_id in device.config_entries:
64+
config_entry = call.hass.config_entries.async_get_entry(entry_id)
65+
if config_entry is not None and config_entry.domain == DOMAIN:
66+
break
67+
nintendo_device_id = _get_nintendo_device_id(device)
68+
if config_entry and nintendo_device_id:
69+
await config_entry.runtime_data.api.devices[nintendo_device_id].add_extra_time(
70+
bonus_time
71+
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
add_bonus_time:
2+
fields:
3+
bonus_time:
4+
required: true
5+
example: 30
6+
selector:
7+
number:
8+
min: -1
9+
max: 1440
10+
unit_of_measurement: minutes
11+
mode: box
12+
device_id:
13+
required: true
14+
example: 1234567890abcdef1234567890abcdef
15+
selector:
16+
device:
17+
integration: nintendo_parental_controls

homeassistant/components/nintendo_parental_controls/strings.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,29 @@
6161
},
6262
"bedtime_alarm_out_of_range": {
6363
"message": "{value} not accepted. Bedtime Alarm must be between {bedtime_alarm_min} and {bedtime_alarm_max}. To disable, set to {bedtime_alarm_disable}."
64+
},
65+
"config_entry_not_found": {
66+
"message": "Config entry not found."
67+
},
68+
"device_not_found": {
69+
"message": "Device not found."
70+
}
71+
},
72+
"services": {
73+
"add_bonus_time": {
74+
"description": "Add bonus screen time to the selected Nintendo Switch.",
75+
"fields": {
76+
"bonus_time": {
77+
"description": "The amount of bonus time to add in minutes. Maximum is 30 minutes, minimum is 5.",
78+
"name": "Bonus Time"
79+
},
80+
"device_id": {
81+
"description": "The ID of the device to add bonus time to.",
82+
"example": "1234567890abcdef",
83+
"name": "Device"
84+
}
85+
},
86+
"name": "Add Bonus Time"
6487
}
6588
}
6689
}

tests/components/nintendo_parental_controls/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def mock_nintendo_device() -> Device:
3737
mock.today_playing_time = 110
3838
mock.today_time_remaining = 10
3939
mock.bedtime_alarm = time(hour=19)
40+
mock.add_extra_time.return_value = None
4041
mock.set_bedtime_alarm.return_value = None
4142
mock.update_max_daily_playtime.return_value = None
4243
mock.forced_termination_mode = True
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"""Test Nintendo Parental Controls service calls."""
2+
3+
from unittest.mock import AsyncMock
4+
5+
import pytest
6+
7+
from homeassistant.components.nintendo_parental_controls.const import (
8+
ATTR_BONUS_TIME,
9+
DOMAIN,
10+
)
11+
from homeassistant.components.nintendo_parental_controls.services import (
12+
NintendoParentalServices,
13+
)
14+
from homeassistant.const import ATTR_DEVICE_ID
15+
from homeassistant.core import HomeAssistant
16+
from homeassistant.exceptions import HomeAssistantError
17+
from homeassistant.helpers import device_registry as dr
18+
19+
from . import setup_integration
20+
21+
from tests.common import MockConfigEntry
22+
23+
24+
async def test_add_bonus_time(
25+
hass: HomeAssistant,
26+
device_registry: dr.DeviceRegistry,
27+
mock_config_entry: MockConfigEntry,
28+
mock_nintendo_client: AsyncMock,
29+
mock_nintendo_device: AsyncMock,
30+
) -> None:
31+
"""Test add bonus time service."""
32+
await setup_integration(hass, mock_config_entry)
33+
device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "testdevid")})
34+
assert device_entry
35+
await hass.services.async_call(
36+
DOMAIN,
37+
NintendoParentalServices.ADD_BONUS_TIME,
38+
{
39+
ATTR_DEVICE_ID: device_entry.id,
40+
ATTR_BONUS_TIME: 15,
41+
},
42+
blocking=True,
43+
)
44+
assert len(mock_nintendo_device.add_extra_time.mock_calls) == 1
45+
46+
47+
async def test_add_bonus_time_invalid_device(
48+
hass: HomeAssistant,
49+
mock_config_entry: MockConfigEntry,
50+
mock_nintendo_client: AsyncMock,
51+
) -> None:
52+
"""Test add bonus time service."""
53+
await setup_integration(hass, mock_config_entry)
54+
with pytest.raises(HomeAssistantError) as err:
55+
await hass.services.async_call(
56+
DOMAIN,
57+
NintendoParentalServices.ADD_BONUS_TIME,
58+
{
59+
ATTR_DEVICE_ID: "invalid_device_id",
60+
ATTR_BONUS_TIME: 15,
61+
},
62+
blocking=True,
63+
)
64+
assert err.value.translation_domain == DOMAIN
65+
assert err.value.translation_key == "device_not_found"

0 commit comments

Comments
 (0)