"""Test Roborock Time platform.""" from collections.abc import Callable from datetime import time, timedelta from typing import Any from freezegun.api import FrozenDateTimeFactory import pytest import roborock from roborock.data import DnDTimer, RoborockBaseTimer, ValleyElectricityTimer from homeassistant.components.time import SERVICE_SET_VALUE from homeassistant.const import STATE_UNKNOWN, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from .conftest import FakeDevice from tests.common import MockConfigEntry, async_fire_time_changed @pytest.fixture def platforms() -> list[Platform]: """Fixture to set platforms used in the test.""" return [Platform.TIME] @pytest.mark.usefixtures("entity_registry_enabled_by_default") @pytest.mark.parametrize( ("entity_id", "start_state", "expected_call", "expected_args", "end_state"), [ ( "time.roborock_s7_maxv_do_not_disturb_begin", "22:00:00", lambda x: x.v1_properties.dnd.set_dnd_timer, DnDTimer(start_hour=1, start_minute=1, end_hour=7, end_minute=0, enabled=1), "01:01:00", ), ( "time.roborock_s7_maxv_do_not_disturb_end", "07:00:00", lambda x: x.v1_properties.dnd.set_dnd_timer, DnDTimer( start_hour=22, start_minute=0, end_hour=1, end_minute=1, enabled=1 ), "01:01:00", ), ( "time.roborock_s7_maxv_off_peak_start", "23:00:00", lambda x: x.v1_properties.valley_electricity_timer.set_timer, ValleyElectricityTimer( start_hour=1, start_minute=1, end_hour=7, end_minute=0, enabled=1 ), "01:01:00", ), ( "time.roborock_s7_maxv_off_peak_end", "07:00:00", lambda x: x.v1_properties.valley_electricity_timer.set_timer, ValleyElectricityTimer( start_hour=23, start_minute=0, end_hour=1, end_minute=1, enabled=1 ), "01:01:00", ), ], ) async def test_update_success( hass: HomeAssistant, setup_entry: MockConfigEntry, fake_vacuum: FakeDevice, entity_id: str, start_state: str, end_state: str, expected_call: Callable[[FakeDevice], Any], expected_args: RoborockBaseTimer, ) -> None: """Test turning switch entities on and off.""" # Ensure that the entity exist, as these test can pass even if there is no entity. state = hass.states.get(entity_id) assert state is not None assert state.state == start_state await hass.services.async_call( "time", SERVICE_SET_VALUE, service_data={"time": time(hour=1, minute=1)}, blocking=True, target={"entity_id": entity_id}, ) call = expected_call(fake_vacuum) assert call.call_count == 1 # Since we update the begin or end time separately: Verify that # the args are built properly by reading the existing value and # only updating the relevant fields. assert call.call_args == ((expected_args,),) state = hass.states.get(entity_id) assert state is not None assert state.state == end_state @pytest.mark.parametrize( ("entity_id"), [ ("time.roborock_s7_maxv_do_not_disturb_begin"), ], ) async def test_update_failure( hass: HomeAssistant, setup_entry: MockConfigEntry, entity_id: str, fake_vacuum: FakeDevice, ) -> None: """Test turning switch entities on and off.""" # Ensure that the entity exist, as these test can pass even if there is no entity. assert hass.states.get(entity_id) is not None fake_vacuum.v1_properties.dnd.set_dnd_timer.side_effect = ( roborock.exceptions.RoborockTimeout ) with pytest.raises(HomeAssistantError, match="Failed to update Roborock options"): await hass.services.async_call( "time", SERVICE_SET_VALUE, service_data={"time": time(hour=1, minute=1)}, blocking=True, target={"entity_id": entity_id}, ) assert fake_vacuum.v1_properties.dnd.set_dnd_timer.call_count == 1 @pytest.mark.usefixtures("entity_registry_enabled_by_default") @pytest.mark.parametrize( ("entity_id", "trait", "missing_attribute"), [ ( "time.roborock_s7_maxv_do_not_disturb_begin", lambda x: x.v1_properties.dnd, "start_hour", ), ( "time.roborock_s7_maxv_do_not_disturb_begin", lambda x: x.v1_properties.dnd, "start_minute", ), ( "time.roborock_s7_maxv_do_not_disturb_end", lambda x: x.v1_properties.dnd, "end_hour", ), ( "time.roborock_s7_maxv_off_peak_start", lambda x: x.v1_properties.valley_electricity_timer, "start_minute", ), ], ) async def test_missing_value( hass: HomeAssistant, freezer: FrozenDateTimeFactory, setup_entry: MockConfigEntry, fake_vacuum: FakeDevice, entity_id: str, trait: Callable[[FakeDevice], Any], missing_attribute: str, ) -> None: """Test that a missing time component reports as unknown instead of raising.""" state = hass.states.get(entity_id) assert state is not None assert state.state != STATE_UNKNOWN setattr(trait(fake_vacuum), missing_attribute, None) freezer.tick(timedelta(seconds=31)) async_fire_time_changed(hass) await hass.async_block_till_done() state = hass.states.get(entity_id) assert state is not None assert state.state == STATE_UNKNOWN