Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
Platform.TIME,
Platform.SWITCH,
Platform.NUMBER,
Platform.SELECT,
]

PLATFORM_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
Expand Down
95 changes: 95 additions & 0 deletions homeassistant/components/nintendo_parental_controls/select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""Nintendo Switch Parental Controls select entity definitions."""

from __future__ import annotations

from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from enum import StrEnum
from typing import Any

from pynintendoparental.enum import DeviceTimerMode

from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .coordinator import NintendoParentalControlsConfigEntry, NintendoUpdateCoordinator
from .entity import Device, NintendoDevice

PARALLEL_UPDATES = 1


class NintendoParentalSelect(StrEnum):
"""Store keys for Nintendo Parental Controls select entities."""

TIMER_MODE = "timer_mode"


@dataclass(kw_only=True, frozen=True)
class NintendoParentalControlsSelectEntityDescription(SelectEntityDescription):
"""Description for Nintendo Parental Controls select entities."""

get_option: Callable[[Device], DeviceTimerMode | None]
set_option_fn: Callable[[Device, DeviceTimerMode], Coroutine[Any, Any, None]]
options_enum: type[DeviceTimerMode]


SELECT_DESCRIPTIONS: tuple[NintendoParentalControlsSelectEntityDescription, ...] = (
NintendoParentalControlsSelectEntityDescription(
key=NintendoParentalSelect.TIMER_MODE,
translation_key=NintendoParentalSelect.TIMER_MODE,
get_option=lambda device: device.timer_mode,
set_option_fn=lambda device, option: device.set_timer_mode(option),
options_enum=DeviceTimerMode,
),
)


async def async_setup_entry(
hass: HomeAssistant,
entry: NintendoParentalControlsConfigEntry,
async_add_devices: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the select platform."""
async_add_devices(
NintendoParentalSelectEntity(
coordinator=entry.runtime_data,
device=device,
description=description,
)
for device in entry.runtime_data.api.devices.values()
for description in SELECT_DESCRIPTIONS
)


class NintendoParentalSelectEntity(NintendoDevice, SelectEntity):
"""Nintendo Parental Controls select entity."""

entity_description: NintendoParentalControlsSelectEntityDescription

def __init__(
self,
coordinator: NintendoUpdateCoordinator,
device: Device,
description: NintendoParentalControlsSelectEntityDescription,
) -> None:
"""Initialize the select entity."""
super().__init__(coordinator=coordinator, device=device, key=description.key)
self.entity_description = description

@property
def current_option(self) -> str | None:
"""Return the current selected option."""
option = self.entity_description.get_option(self._device)
return option.name.lower() if option else None

@property
def options(self) -> list[str]:
"""Return a list of available options."""
return [option.name.lower() for option in self.entity_description.options_enum]

async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
enum_option = self.entity_description.options_enum[option.upper()]
await self.entity_description.set_option_fn(self._device, enum_option)
await self.coordinator.async_request_refresh()
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@
"name": "Max screentime today"
}
},
"select": {
"timer_mode": {
"name": "Restriction mode",
"state": {
"daily": "Same for all days",
"each_day_of_the_week": "Different for each day"
}
}
},
"sensor": {
"playing_time": {
"name": "Used screen time"
Expand Down
3 changes: 3 additions & 0 deletions tests/components/nintendo_parental_controls/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from pynintendoparental import NintendoParental
from pynintendoparental.device import Device
from pynintendoparental.enum import DeviceTimerMode
import pytest

from homeassistant.components.nintendo_parental_controls.const import DOMAIN
Expand Down Expand Up @@ -39,9 +40,11 @@ def mock_nintendo_device() -> Device:
mock.today_playing_time = 110
mock.today_time_remaining = 10
mock.bedtime_alarm = time(hour=19)
mock.timer_mode = DeviceTimerMode.DAILY
mock.add_extra_time.return_value = None
mock.set_bedtime_alarm.return_value = None
mock.update_max_daily_playtime.return_value = None
mock.set_timer_mode.return_value = None
mock.forced_termination_mode = True
mock.model = "Test Model"
mock.generation = "P00"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# serializer version: 1
# name: test_select[select.home_assistant_test_restriction_mode-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'options': list([
'daily',
'each_day_of_the_week',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'select',
'entity_category': None,
'entity_id': 'select.home_assistant_test_restriction_mode',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Restriction mode',
'platform': 'nintendo_parental_controls',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <NintendoParentalSelect.TIMER_MODE: 'timer_mode'>,
'unique_id': 'testdevid_timer_mode',
'unit_of_measurement': None,
})
# ---
# name: test_select[select.home_assistant_test_restriction_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Home Assistant Test Restriction mode',
'options': list([
'daily',
'each_day_of_the_week',
]),
}),
'context': <ANY>,
'entity_id': 'select.home_assistant_test_restriction_mode',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'daily',
})
# ---
64 changes: 64 additions & 0 deletions tests/components/nintendo_parental_controls/test_select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Tests for Nintendo Switch Parental Controls select platform."""

from unittest.mock import AsyncMock, patch

from pynintendoparental.enum import DeviceTimerMode
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.select import (
ATTR_OPTION,
DOMAIN as SELECT_DOMAIN,
SERVICE_SELECT_OPTION,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er

from . import setup_integration

from tests.common import MockConfigEntry, snapshot_platform


async def test_select(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_nintendo_client: AsyncMock,
mock_nintendo_device: AsyncMock,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test select platform."""
with patch(
"homeassistant.components.nintendo_parental_controls._PLATFORMS",
[Platform.SELECT],
):
await setup_integration(hass, mock_config_entry)

await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)


async def test_select_option(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_nintendo_client: AsyncMock,
mock_nintendo_device: AsyncMock,
) -> None:
"""Test select option service."""
with patch(
"homeassistant.components.nintendo_parental_controls._PLATFORMS",
[Platform.SELECT],
):
await setup_integration(hass, mock_config_entry)

await hass.services.async_call(
SELECT_DOMAIN,
SERVICE_SELECT_OPTION,
{
ATTR_ENTITY_ID: "select.home_assistant_test_restriction_mode",
ATTR_OPTION: DeviceTimerMode.EACH_DAY_OF_THE_WEEK.name.lower(),
},
blocking=True,
)
mock_nintendo_device.set_timer_mode.assert_awaited_once_with(
DeviceTimerMode.EACH_DAY_OF_THE_WEEK
)
Loading