From af2d2a857a2d8be8641b0adba0b9711d005b8630 Mon Sep 17 00:00:00 2001 From: Jordan Harvey Date: Sat, 14 Feb 2026 21:51:20 +0000 Subject: [PATCH] Add bedtime end time entity Nintendo parental controls (#160927) --- .../nintendo_parental_controls/const.py | 3 ++ .../nintendo_parental_controls/strings.json | 6 +++ .../nintendo_parental_controls/time.py | 30 ++++++++++- .../nintendo_parental_controls/conftest.py | 2 + .../snapshots/test_time.ambr | 49 +++++++++++++++++ .../nintendo_parental_controls/test_time.py | 53 ++++++++++++++++--- 6 files changed, 134 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/nintendo_parental_controls/const.py b/homeassistant/components/nintendo_parental_controls/const.py index a9496e85a0b..e57d3118fea 100644 --- a/homeassistant/components/nintendo_parental_controls/const.py +++ b/homeassistant/components/nintendo_parental_controls/const.py @@ -8,6 +8,9 @@ BEDTIME_ALARM_MIN = "16:00" BEDTIME_ALARM_MAX = "23:00" BEDTIME_ALARM_DISABLE = "00:00" +BEDTIME_END_TIME_MIN = "05:00" +BEDTIME_END_TIME_MAX = "09:00" + APP_SETUP_URL = ( "https://www.nintendo.com/my/support/switch/parentalcontrols/app/setup.html" ) diff --git a/homeassistant/components/nintendo_parental_controls/strings.json b/homeassistant/components/nintendo_parental_controls/strings.json index 8b671d4b099..cde905574a2 100644 --- a/homeassistant/components/nintendo_parental_controls/strings.json +++ b/homeassistant/components/nintendo_parental_controls/strings.json @@ -65,6 +65,9 @@ "time": { "bedtime_alarm": { "name": "Bedtime alarm" + }, + "bedtime_end_time": { + "name": "Bedtime end time" } } }, @@ -75,6 +78,9 @@ "bedtime_alarm_out_of_range": { "message": "{value} not accepted. Bedtime Alarm must be between {bedtime_alarm_min} and {bedtime_alarm_max}. To disable, set to {bedtime_alarm_disable}." }, + "bedtime_end_time_out_of_range": { + "message": "{value} not accepted. Bedtime End Time must be between {bedtime_end_time_min} and {bedtime_end_time_max}. To disable, set to {bedtime_alarm_disable}." + }, "config_entry_not_found": { "message": "Config entry not found." }, diff --git a/homeassistant/components/nintendo_parental_controls/time.py b/homeassistant/components/nintendo_parental_controls/time.py index 9c898e1f8ca..e1c94006707 100644 --- a/homeassistant/components/nintendo_parental_controls/time.py +++ b/homeassistant/components/nintendo_parental_controls/time.py @@ -16,7 +16,14 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import BEDTIME_ALARM_DISABLE, BEDTIME_ALARM_MAX, BEDTIME_ALARM_MIN, DOMAIN +from .const import ( + BEDTIME_ALARM_DISABLE, + BEDTIME_ALARM_MAX, + BEDTIME_ALARM_MIN, + BEDTIME_END_TIME_MAX, + BEDTIME_END_TIME_MIN, + DOMAIN, +) from .coordinator import NintendoParentalControlsConfigEntry, NintendoUpdateCoordinator from .entity import Device, NintendoDevice @@ -30,6 +37,7 @@ class NintendoParentalControlsTime(StrEnum): """Store keys for Nintendo Parental time.""" BEDTIME_ALARM = "bedtime_alarm" + BEDTIME_END_TIME = "bedtime_end_time" @dataclass(kw_only=True, frozen=True) @@ -47,6 +55,12 @@ TIME_DESCRIPTIONS: tuple[NintendoParentalControlsTimeEntityDescription, ...] = ( value_fn=lambda device: device.bedtime_alarm, set_value_fn=lambda device, value: device.set_bedtime_alarm(value=value), ), + NintendoParentalControlsTimeEntityDescription( + key=NintendoParentalControlsTime.BEDTIME_END_TIME, + translation_key=NintendoParentalControlsTime.BEDTIME_END_TIME, + value_fn=lambda device: device.bedtime_end, + set_value_fn=lambda device, value: device.set_bedtime_end_time(value=value), + ), ) @@ -88,6 +102,20 @@ class NintendoParentalControlsTimeEntity(NintendoDevice, TimeEntity): try: await self.entity_description.set_value_fn(self._device, value) except BedtimeOutOfRangeError as exc: + if ( + self.entity_description.key + == NintendoParentalControlsTime.BEDTIME_END_TIME + ): + raise ServiceValidationError( + translation_domain=DOMAIN, + translation_key="bedtime_end_time_out_of_range", + translation_placeholders={ + "value": value.strftime("%H:%M"), + "bedtime_end_time_max": BEDTIME_END_TIME_MAX, + "bedtime_end_time_min": BEDTIME_END_TIME_MIN, + "bedtime_alarm_disable": BEDTIME_ALARM_DISABLE, + }, + ) from exc raise ServiceValidationError( translation_domain=DOMAIN, translation_key="bedtime_alarm_out_of_range", diff --git a/tests/components/nintendo_parental_controls/conftest.py b/tests/components/nintendo_parental_controls/conftest.py index 3b7ccf0f4b0..97ee7ccb2c1 100644 --- a/tests/components/nintendo_parental_controls/conftest.py +++ b/tests/components/nintendo_parental_controls/conftest.py @@ -39,11 +39,13 @@ def mock_nintendo_device() -> Device: mock.limit_time = 120 mock.today_playing_time = 110 mock.today_time_remaining = 10 + mock.bedtime_end = time(hour=7) mock.bedtime_alarm = time(hour=19) mock.timer_mode = DeviceTimerMode.DAILY mock.extra_playing_time = 30 mock.add_extra_time.return_value = None mock.set_bedtime_alarm.return_value = None + mock.set_bedtime_end_time.return_value = None mock.update_max_daily_playtime.return_value = None mock.set_timer_mode.return_value = None mock.forced_termination_mode = True diff --git a/tests/components/nintendo_parental_controls/snapshots/test_time.ambr b/tests/components/nintendo_parental_controls/snapshots/test_time.ambr index 8f55558272f..90729dce9e1 100644 --- a/tests/components/nintendo_parental_controls/snapshots/test_time.ambr +++ b/tests/components/nintendo_parental_controls/snapshots/test_time.ambr @@ -48,3 +48,52 @@ 'state': '19:00:00', }) # --- +# name: test_time[time.home_assistant_test_bedtime_end_time-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'time', + 'entity_category': None, + 'entity_id': 'time.home_assistant_test_bedtime_end_time', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Bedtime end time', + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Bedtime end time', + 'platform': 'nintendo_parental_controls', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': , + 'unique_id': 'testdevid_bedtime_end_time', + 'unit_of_measurement': None, + }) +# --- +# name: test_time[time.home_assistant_test_bedtime_end_time-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'Home Assistant Test Bedtime end time', + }), + 'context': , + 'entity_id': 'time.home_assistant_test_bedtime_end_time', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '07:00:00', + }) +# --- diff --git a/tests/components/nintendo_parental_controls/test_time.py b/tests/components/nintendo_parental_controls/test_time.py index c0674e27ab4..f5b84f619df 100644 --- a/tests/components/nintendo_parental_controls/test_time.py +++ b/tests/components/nintendo_parental_controls/test_time.py @@ -38,11 +38,25 @@ async def test_time( await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) +@pytest.mark.parametrize( + ("entity_id", "new_value", "called_function_name"), + [ + ("time.home_assistant_test_bedtime_alarm", "20:00:00", "set_bedtime_alarm"), + ( + "time.home_assistant_test_bedtime_end_time", + "06:30:00", + "set_bedtime_end_time", + ), + ], +) async def test_set_time( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_nintendo_client: AsyncMock, mock_nintendo_device: AsyncMock, + entity_id: str, + new_value: str, + called_function_name: str, ) -> None: """Test time platform service validation errors.""" with patch( @@ -53,21 +67,44 @@ async def test_set_time( await hass.services.async_call( TIME_DOMAIN, SERVICE_SET_VALUE, - service_data={ATTR_TIME: "20:00:00"}, - target={ATTR_ENTITY_ID: "time.home_assistant_test_bedtime_alarm"}, + service_data={ATTR_TIME: new_value}, + target={ATTR_ENTITY_ID: entity_id}, blocking=True, ) - assert len(mock_nintendo_device.set_bedtime_alarm.mock_calls) == 1 + assert len(getattr(mock_nintendo_device, called_function_name).mock_calls) == 1 +@pytest.mark.parametrize( + ("entity_id", "new_value", "translation_key", "called_function_name"), + [ + ( + "time.home_assistant_test_bedtime_alarm", + "03:00:00", + "bedtime_alarm_out_of_range", + "set_bedtime_alarm", + ), + ( + "time.home_assistant_test_bedtime_end_time", + "10:00:00", + "bedtime_end_time_out_of_range", + "set_bedtime_end_time", + ), + ], +) async def test_set_time_service_exceptions( hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_nintendo_client: AsyncMock, mock_nintendo_device: AsyncMock, + entity_id: str, + new_value: str, + translation_key: str, + called_function_name: str, ) -> None: """Test time platform service validation errors.""" - mock_nintendo_device.set_bedtime_alarm.side_effect = BedtimeOutOfRangeError(None) + getattr( + mock_nintendo_device, called_function_name + ).side_effect = BedtimeOutOfRangeError(None) with patch( "homeassistant.components.nintendo_parental_controls._PLATFORMS", [Platform.TIME], @@ -77,9 +114,9 @@ async def test_set_time_service_exceptions( await hass.services.async_call( TIME_DOMAIN, SERVICE_SET_VALUE, - service_data={ATTR_TIME: "01:00:00"}, - target={ATTR_ENTITY_ID: "time.home_assistant_test_bedtime_alarm"}, + service_data={ATTR_TIME: new_value}, + target={ATTR_ENTITY_ID: entity_id}, blocking=True, ) - assert len(mock_nintendo_device.set_bedtime_alarm.mock_calls) == 1 - assert err.value.translation_key == "bedtime_alarm_out_of_range" + assert len(getattr(mock_nintendo_device, called_function_name).mock_calls) == 1 + assert err.value.translation_key == translation_key