Skip to content

Commit 1452aec

Browse files
authored
Add switch platform to Nintendo Parental controls integration (home-assistant#154179)
1 parent 6f8439d commit 1452aec

File tree

6 files changed

+215
-1
lines changed

6 files changed

+215
-1
lines changed

homeassistant/components/nintendo_parental_controls/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from .const import CONF_SESSION_TOKEN, DOMAIN
1717
from .coordinator import NintendoParentalControlsConfigEntry, NintendoUpdateCoordinator
1818

19-
_PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.TIME]
19+
_PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.TIME, Platform.SWITCH]
2020

2121

2222
async def async_setup_entry(

homeassistant/components/nintendo_parental_controls/strings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@
4343
"bedtime_alarm": {
4444
"name": "Bedtime alarm"
4545
}
46+
},
47+
"switch": {
48+
"suspend_software": {
49+
"name": "Suspend software"
50+
}
4651
}
4752
},
4853
"exceptions": {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""Switch platform for Nintendo Parental."""
2+
3+
from __future__ import annotations
4+
5+
from collections.abc import Callable, Coroutine
6+
from dataclasses import dataclass
7+
from enum import StrEnum
8+
from typing import Any
9+
10+
from pynintendoparental.enum import RestrictionMode
11+
12+
from homeassistant.components.switch import (
13+
SwitchDeviceClass,
14+
SwitchEntity,
15+
SwitchEntityDescription,
16+
)
17+
from homeassistant.core import HomeAssistant
18+
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
19+
20+
from .coordinator import NintendoParentalControlsConfigEntry, NintendoUpdateCoordinator
21+
from .entity import Device, NintendoDevice
22+
23+
PARALLEL_UPDATES = 0
24+
25+
26+
class NintendoParentalSwitch(StrEnum):
27+
"""Store keys for Nintendo Parental Controls switches."""
28+
29+
SUSPEND_SOFTWARE = "suspend_software"
30+
31+
32+
@dataclass(kw_only=True, frozen=True)
33+
class NintendoParentalControlsSwitchEntityDescription(SwitchEntityDescription):
34+
"""Description for Nintendo Parental Controls switch entities."""
35+
36+
is_on: Callable[[Device], bool | None]
37+
turn_on_fn: Callable[[Device], Coroutine[Any, Any, None]]
38+
turn_off_fn: Callable[[Device], Coroutine[Any, Any, None]]
39+
40+
41+
SWITCH_DESCRIPTIONS: tuple[NintendoParentalControlsSwitchEntityDescription, ...] = (
42+
NintendoParentalControlsSwitchEntityDescription(
43+
key=NintendoParentalSwitch.SUSPEND_SOFTWARE,
44+
translation_key=NintendoParentalSwitch.SUSPEND_SOFTWARE,
45+
device_class=SwitchDeviceClass.SWITCH,
46+
is_on=lambda device: device.forced_termination_mode,
47+
turn_off_fn=lambda device: device.set_restriction_mode(RestrictionMode.ALARM),
48+
turn_on_fn=lambda device: device.set_restriction_mode(
49+
RestrictionMode.FORCED_TERMINATION
50+
),
51+
),
52+
)
53+
54+
55+
async def async_setup_entry(
56+
hass: HomeAssistant,
57+
entry: NintendoParentalControlsConfigEntry,
58+
async_add_devices: AddConfigEntryEntitiesCallback,
59+
) -> None:
60+
"""Set up the switch platform."""
61+
async_add_devices(
62+
NintendoParentalControlsSwitchEntity(entry.runtime_data, device, switch)
63+
for device in entry.runtime_data.api.devices.values()
64+
for switch in SWITCH_DESCRIPTIONS
65+
)
66+
67+
68+
class NintendoParentalControlsSwitchEntity(NintendoDevice, SwitchEntity):
69+
"""Represent a single switch."""
70+
71+
entity_description: NintendoParentalControlsSwitchEntityDescription
72+
73+
def __init__(
74+
self,
75+
coordinator: NintendoUpdateCoordinator,
76+
device: Device,
77+
description: NintendoParentalControlsSwitchEntityDescription,
78+
) -> None:
79+
"""Initialize the switch."""
80+
super().__init__(coordinator=coordinator, device=device, key=description.key)
81+
self.entity_description = description
82+
83+
@property
84+
def is_on(self) -> bool | None:
85+
"""Return entity state."""
86+
return self.entity_description.is_on(self._device)
87+
88+
async def async_turn_on(self, **kwargs: Any) -> None:
89+
"""Turn the switch on."""
90+
await self.entity_description.turn_on_fn(self._device)
91+
92+
async def async_turn_off(self, **kwargs: Any) -> None:
93+
"""Turn the switch off."""
94+
await self.entity_description.turn_off_fn(self._device)

tests/components/nintendo_parental_controls/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def mock_nintendo_device() -> Device:
3636
mock.today_playing_time = 110
3737
mock.bedtime_alarm = time(hour=19)
3838
mock.set_bedtime_alarm.return_value = None
39+
mock.forced_termination_mode = True
3940
return mock
4041

4142

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# serializer version: 1
2+
# name: test_switch[switch.home_assistant_test_suspend_software-entry]
3+
EntityRegistryEntrySnapshot({
4+
'aliases': set({
5+
}),
6+
'area_id': None,
7+
'capabilities': None,
8+
'config_entry_id': <ANY>,
9+
'config_subentry_id': <ANY>,
10+
'device_class': None,
11+
'device_id': <ANY>,
12+
'disabled_by': None,
13+
'domain': 'switch',
14+
'entity_category': None,
15+
'entity_id': 'switch.home_assistant_test_suspend_software',
16+
'has_entity_name': True,
17+
'hidden_by': None,
18+
'icon': None,
19+
'id': <ANY>,
20+
'labels': set({
21+
}),
22+
'name': None,
23+
'options': dict({
24+
}),
25+
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
26+
'original_icon': None,
27+
'original_name': 'Suspend software',
28+
'platform': 'nintendo_parental_controls',
29+
'previous_unique_id': None,
30+
'suggested_object_id': None,
31+
'supported_features': 0,
32+
'translation_key': <NintendoParentalSwitch.SUSPEND_SOFTWARE: 'suspend_software'>,
33+
'unique_id': 'testdevid_suspend_software',
34+
'unit_of_measurement': None,
35+
})
36+
# ---
37+
# name: test_switch[switch.home_assistant_test_suspend_software-state]
38+
StateSnapshot({
39+
'attributes': ReadOnlyDict({
40+
'device_class': 'switch',
41+
'friendly_name': 'Home Assistant Test Suspend software',
42+
}),
43+
'context': <ANY>,
44+
'entity_id': 'switch.home_assistant_test_suspend_software',
45+
'last_changed': <ANY>,
46+
'last_reported': <ANY>,
47+
'last_updated': <ANY>,
48+
'state': 'on',
49+
})
50+
# ---
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""Test switch platform."""
2+
3+
from unittest.mock import AsyncMock, patch
4+
5+
from syrupy.assertion import SnapshotAssertion
6+
7+
from homeassistant.components.switch import (
8+
DOMAIN as SWITCH_DOMAIN,
9+
SERVICE_TURN_OFF,
10+
SERVICE_TURN_ON,
11+
)
12+
from homeassistant.const import ATTR_ENTITY_ID, Platform
13+
from homeassistant.core import HomeAssistant
14+
from homeassistant.helpers import entity_registry as er
15+
16+
from . import setup_integration
17+
18+
from tests.common import MockConfigEntry, snapshot_platform
19+
20+
21+
async def test_switch(
22+
hass: HomeAssistant,
23+
mock_config_entry: MockConfigEntry,
24+
mock_nintendo_client: AsyncMock,
25+
entity_registry: er.EntityRegistry,
26+
snapshot: SnapshotAssertion,
27+
) -> None:
28+
"""Test switch platform."""
29+
with patch(
30+
"homeassistant.components.nintendo_parental_controls._PLATFORMS",
31+
[Platform.SWITCH],
32+
):
33+
await setup_integration(hass, mock_config_entry)
34+
35+
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
36+
37+
38+
async def test_suspend_software(
39+
hass: HomeAssistant,
40+
mock_config_entry: MockConfigEntry,
41+
mock_nintendo_client: AsyncMock,
42+
mock_nintendo_device: AsyncMock,
43+
) -> None:
44+
"""Test switch platform."""
45+
with patch(
46+
"homeassistant.components.nintendo_parental_controls._PLATFORMS",
47+
[Platform.SWITCH],
48+
):
49+
await setup_integration(hass, mock_config_entry)
50+
51+
await hass.services.async_call(
52+
SWITCH_DOMAIN,
53+
SERVICE_TURN_ON,
54+
target={ATTR_ENTITY_ID: "switch.home_assistant_test_suspend_software"},
55+
blocking=True,
56+
)
57+
assert len(mock_nintendo_device.set_restriction_mode.mock_calls) == 1
58+
await hass.services.async_call(
59+
SWITCH_DOMAIN,
60+
SERVICE_TURN_OFF,
61+
target={ATTR_ENTITY_ID: "switch.home_assistant_test_suspend_software"},
62+
blocking=True,
63+
)
64+
assert len(mock_nintendo_device.set_restriction_mode.mock_calls) == 2

0 commit comments

Comments
 (0)