1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

Add bedtime end time entity Nintendo parental controls (#160927)

This commit is contained in:
Jordan Harvey
2026-02-14 21:51:20 +00:00
committed by GitHub
parent 31970255a2
commit af2d2a857a
6 changed files with 134 additions and 9 deletions

View File

@@ -8,6 +8,9 @@ BEDTIME_ALARM_MIN = "16:00"
BEDTIME_ALARM_MAX = "23:00" BEDTIME_ALARM_MAX = "23:00"
BEDTIME_ALARM_DISABLE = "00:00" BEDTIME_ALARM_DISABLE = "00:00"
BEDTIME_END_TIME_MIN = "05:00"
BEDTIME_END_TIME_MAX = "09:00"
APP_SETUP_URL = ( APP_SETUP_URL = (
"https://www.nintendo.com/my/support/switch/parentalcontrols/app/setup.html" "https://www.nintendo.com/my/support/switch/parentalcontrols/app/setup.html"
) )

View File

@@ -65,6 +65,9 @@
"time": { "time": {
"bedtime_alarm": { "bedtime_alarm": {
"name": "Bedtime alarm" "name": "Bedtime alarm"
},
"bedtime_end_time": {
"name": "Bedtime end time"
} }
} }
}, },
@@ -75,6 +78,9 @@
"bedtime_alarm_out_of_range": { "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}." "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": { "config_entry_not_found": {
"message": "Config entry not found." "message": "Config entry not found."
}, },

View File

@@ -16,7 +16,14 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback 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 .coordinator import NintendoParentalControlsConfigEntry, NintendoUpdateCoordinator
from .entity import Device, NintendoDevice from .entity import Device, NintendoDevice
@@ -30,6 +37,7 @@ class NintendoParentalControlsTime(StrEnum):
"""Store keys for Nintendo Parental time.""" """Store keys for Nintendo Parental time."""
BEDTIME_ALARM = "bedtime_alarm" BEDTIME_ALARM = "bedtime_alarm"
BEDTIME_END_TIME = "bedtime_end_time"
@dataclass(kw_only=True, frozen=True) @dataclass(kw_only=True, frozen=True)
@@ -47,6 +55,12 @@ TIME_DESCRIPTIONS: tuple[NintendoParentalControlsTimeEntityDescription, ...] = (
value_fn=lambda device: device.bedtime_alarm, value_fn=lambda device: device.bedtime_alarm,
set_value_fn=lambda device, value: device.set_bedtime_alarm(value=value), 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: try:
await self.entity_description.set_value_fn(self._device, value) await self.entity_description.set_value_fn(self._device, value)
except BedtimeOutOfRangeError as exc: 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( raise ServiceValidationError(
translation_domain=DOMAIN, translation_domain=DOMAIN,
translation_key="bedtime_alarm_out_of_range", translation_key="bedtime_alarm_out_of_range",

View File

@@ -39,11 +39,13 @@ def mock_nintendo_device() -> Device:
mock.limit_time = 120 mock.limit_time = 120
mock.today_playing_time = 110 mock.today_playing_time = 110
mock.today_time_remaining = 10 mock.today_time_remaining = 10
mock.bedtime_end = time(hour=7)
mock.bedtime_alarm = time(hour=19) mock.bedtime_alarm = time(hour=19)
mock.timer_mode = DeviceTimerMode.DAILY mock.timer_mode = DeviceTimerMode.DAILY
mock.extra_playing_time = 30 mock.extra_playing_time = 30
mock.add_extra_time.return_value = None mock.add_extra_time.return_value = None
mock.set_bedtime_alarm.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.update_max_daily_playtime.return_value = None
mock.set_timer_mode.return_value = None mock.set_timer_mode.return_value = None
mock.forced_termination_mode = True mock.forced_termination_mode = True

View File

@@ -48,3 +48,52 @@
'state': '19:00:00', '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': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'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': <ANY>,
'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': <NintendoParentalControlsTime.BEDTIME_END_TIME: 'bedtime_end_time'>,
'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': <ANY>,
'entity_id': 'time.home_assistant_test_bedtime_end_time',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '07:00:00',
})
# ---

View File

@@ -38,11 +38,25 @@ async def test_time(
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) 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( async def test_set_time(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_nintendo_client: AsyncMock, mock_nintendo_client: AsyncMock,
mock_nintendo_device: AsyncMock, mock_nintendo_device: AsyncMock,
entity_id: str,
new_value: str,
called_function_name: str,
) -> None: ) -> None:
"""Test time platform service validation errors.""" """Test time platform service validation errors."""
with patch( with patch(
@@ -53,21 +67,44 @@ async def test_set_time(
await hass.services.async_call( await hass.services.async_call(
TIME_DOMAIN, TIME_DOMAIN,
SERVICE_SET_VALUE, SERVICE_SET_VALUE,
service_data={ATTR_TIME: "20:00:00"}, service_data={ATTR_TIME: new_value},
target={ATTR_ENTITY_ID: "time.home_assistant_test_bedtime_alarm"}, target={ATTR_ENTITY_ID: entity_id},
blocking=True, 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( async def test_set_time_service_exceptions(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_nintendo_client: AsyncMock, mock_nintendo_client: AsyncMock,
mock_nintendo_device: AsyncMock, mock_nintendo_device: AsyncMock,
entity_id: str,
new_value: str,
translation_key: str,
called_function_name: str,
) -> None: ) -> None:
"""Test time platform service validation errors.""" """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( with patch(
"homeassistant.components.nintendo_parental_controls._PLATFORMS", "homeassistant.components.nintendo_parental_controls._PLATFORMS",
[Platform.TIME], [Platform.TIME],
@@ -77,9 +114,9 @@ async def test_set_time_service_exceptions(
await hass.services.async_call( await hass.services.async_call(
TIME_DOMAIN, TIME_DOMAIN,
SERVICE_SET_VALUE, SERVICE_SET_VALUE,
service_data={ATTR_TIME: "01:00:00"}, service_data={ATTR_TIME: new_value},
target={ATTR_ENTITY_ID: "time.home_assistant_test_bedtime_alarm"}, target={ATTR_ENTITY_ID: entity_id},
blocking=True, 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
assert err.value.translation_key == "bedtime_alarm_out_of_range" assert err.value.translation_key == translation_key