"""The tests for Netatmo light.""" from typing import Any from unittest.mock import AsyncMock, patch from syrupy.assertion import SnapshotAssertion from homeassistant.components.light import ( DOMAIN as LIGHT_DOMAIN, SERVICE_TURN_OFF, SERVICE_TURN_ON, ) from homeassistant.components.netatmo import DOMAIN from homeassistant.const import ATTR_ENTITY_ID, CONF_WEBHOOK_ID, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from .common import ( FAKE_WEBHOOK_ACTIVATION, selected_platforms, simulate_webhook, snapshot_platform_entities, ) from tests.common import MockConfigEntry from tests.test_util.aiohttp import AiohttpClientMockResponse async def test_entity( hass: HomeAssistant, config_entry: MockConfigEntry, netatmo_auth: AsyncMock, snapshot: SnapshotAssertion, entity_registry: er.EntityRegistry, ) -> None: """Test entities.""" await snapshot_platform_entities( hass, config_entry, Platform.LIGHT, entity_registry, snapshot, ) async def test_camera_light_setup_and_services( hass: HomeAssistant, config_entry: MockConfigEntry, netatmo_auth: AsyncMock ) -> None: """Test camera ligiht setup and services.""" with selected_platforms(["light"]): assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() webhook_id = config_entry.data[CONF_WEBHOOK_ID] # Fake webhook activation await simulate_webhook(hass, webhook_id, FAKE_WEBHOOK_ACTIVATION) await hass.async_block_till_done() light_entity = "light.front" assert hass.states.get(light_entity).state == "unavailable" # Trigger light mode change response = { "event_type": "light_mode", "device_id": "12:34:56:10:b9:0e", "camera_id": "12:34:56:10:b9:0e", "event_id": "601dce1560abca1ebad9b723", "push_type": "NOC-light_mode", "sub_type": "on", } await simulate_webhook(hass, webhook_id, response) assert hass.states.get(light_entity).state == "on" # Trigger light mode change with erroneous webhook data response = { "event_type": "light_mode", "device_id": "12:34:56:10:b9:0e", } await simulate_webhook(hass, webhook_id, response) assert hass.states.get(light_entity).state == "on" # Test turning light off with patch("pyatmo.home.Home.async_set_state") as mock_set_state: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: light_entity}, blocking=True, ) await hass.async_block_till_done() mock_set_state.assert_called_once_with( {"modules": [{"id": "12:34:56:10:b9:0e", "floodlight": "auto"}]} ) # Test turning light on with patch("pyatmo.home.Home.async_set_state") as mock_set_state: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: light_entity}, blocking=True, ) await hass.async_block_till_done() mock_set_state.assert_called_once_with( {"modules": [{"id": "12:34:56:10:b9:0e", "floodlight": "on"}]} ) async def test_setup_component_no_devices(hass: HomeAssistant, config_entry) -> None: """Test setup with no devices.""" fake_post_hits = 0 async def fake_post_request_no_data(*args: Any, **kwargs: Any): """Fake error during requesting backend data.""" nonlocal fake_post_hits fake_post_hits += 1 return AiohttpClientMockResponse( method="POST", url=kwargs["endpoint"], json={}, ) with ( patch( "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth" ) as mock_auth, patch("homeassistant.components.netatmo.coordinator.PLATFORMS", ["light"]), patch( "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook.webhook_generate_url", ), ): mock_auth.return_value.async_post_api_request.side_effect = ( fake_post_request_no_data ) mock_auth.return_value.async_addwebhook.side_effect = AsyncMock() mock_auth.return_value.async_dropwebhook.side_effect = AsyncMock() assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() # Fake webhook activation await simulate_webhook( hass, config_entry.data[CONF_WEBHOOK_ID], FAKE_WEBHOOK_ACTIVATION ) await hass.async_block_till_done() assert fake_post_hits == 3 assert hass.config_entries.async_entries(DOMAIN) assert len(hass.states.async_all()) == 0 async def test_light_setup_and_services( hass: HomeAssistant, config_entry: MockConfigEntry, netatmo_auth: AsyncMock ) -> None: """Test setup and services.""" with selected_platforms(["light"]): assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() light_entity = "light.bathroom_light" assert hass.states.get(light_entity).state == "off" # Test turning light off with patch("pyatmo.home.Home.async_set_state") as mock_set_state: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: light_entity}, blocking=True, ) await hass.async_block_till_done() mock_set_state.assert_called_once_with( { "modules": [ { "id": "12:34:56:00:01:01:01:a1", "on": False, "bridge": "12:34:56:80:60:40", } ] } ) # Test turning light on with patch("pyatmo.home.Home.async_set_state") as mock_set_state: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: light_entity}, blocking=True, ) await hass.async_block_till_done() mock_set_state.assert_called_once_with( { "modules": [ { "id": "12:34:56:00:01:01:01:a1", "on": True, "bridge": "12:34:56:80:60:40", } ] } ) async def test_dimmable_light_turn_on_updates_brightness_optimistically( hass: HomeAssistant, config_entry: MockConfigEntry, netatmo_auth: AsyncMock ) -> None: """Test that turning on a dimmable light with a brightness updates state immediately. The Netatmo API doesn't push brightness changes back until the next poll, which can be several minutes away, so the new brightness must be reflected optimistically instead of waiting for that poll. """ with selected_platforms(["light"]): assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() light_entity = "light.unknown_00_11_22_33_00_11_45_fe" with patch("pyatmo.home.Home.async_set_state") as mock_set_state: await hass.services.async_call( LIGHT_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: light_entity, "brightness": 128}, blocking=True, ) await hass.async_block_till_done() mock_set_state.assert_called_once_with( { "modules": [ { "id": "00:11:22:33:00:11:45:fe", "brightness": round(128 / 2.55), "bridge": "12:34:56:80:60:40", } ] } ) state = hass.states.get(light_entity) assert state.state == "on" assert state.attributes["brightness"] == 128