"""Philips Hue switch platform tests for V2 bridge/api.""" from unittest.mock import Mock import pytest from homeassistant.components.hue.const import DOMAIN from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.util.json import JsonArrayType from .conftest import setup_platform from .const import ( FAKE_BEHAVIOR_INSTANCE, FAKE_BEHAVIOR_SCRIPT, FAKE_BINARY_SENSOR, FAKE_DEVICE, FAKE_PRESENCE_MIMICKING_INSTANCE, FAKE_PRESENCE_MIMICKING_SCRIPT, FAKE_ZIGBEE_CONNECTIVITY, ) async def test_switch( hass: HomeAssistant, mock_bridge_v2: Mock, v2_resources_test_data: JsonArrayType ) -> None: """Test if (config) switches get created.""" await mock_bridge_v2.api.load_test_data(v2_resources_test_data) await setup_platform(hass, mock_bridge_v2, Platform.SWITCH) # there shouldn't have been any requests at this point assert len(mock_bridge_v2.mock_requests) == 0 # 4 entities should be created from test data assert len(hass.states.async_all()) == 4 # test config switch to enable/disable motion sensor test_entity = hass.states.get("switch.hue_motion_sensor_motion_sensor_enabled") assert test_entity is not None assert test_entity.name == "Hue motion sensor Motion sensor enabled" assert test_entity.state == "on" assert test_entity.attributes["device_class"] == "switch" # test config switch to enable/disable a behavior_instance resource (=builtin # automation) test_entity = hass.states.get("switch.philips_hue_automation_timer_test") assert test_entity is not None assert test_entity.name == "Philips hue Automation: Timer Test" assert test_entity.state == "on" assert test_entity.attributes["device_class"] == "switch" async def test_switch_turn_on_service( hass: HomeAssistant, mock_bridge_v2: Mock, v2_resources_test_data: JsonArrayType ) -> None: """Test calling the turn on service on a switch.""" await mock_bridge_v2.api.load_test_data(v2_resources_test_data) await setup_platform(hass, mock_bridge_v2, Platform.SWITCH) test_entity_id = "switch.hue_motion_sensor_motion_sensor_enabled" # call the HA turn_on service await hass.services.async_call( "switch", "turn_on", {"entity_id": test_entity_id}, blocking=True, ) # PUT request should have been sent to device with correct params assert len(mock_bridge_v2.mock_requests) == 1 assert mock_bridge_v2.mock_requests[0]["method"] == "put" assert mock_bridge_v2.mock_requests[0]["json"]["enabled"] is True async def test_switch_turn_off_service( hass: HomeAssistant, mock_bridge_v2: Mock, v2_resources_test_data: JsonArrayType ) -> None: """Test calling the turn off service on a switch.""" await mock_bridge_v2.api.load_test_data(v2_resources_test_data) await setup_platform(hass, mock_bridge_v2, Platform.SWITCH) test_entity_id = "switch.hue_motion_sensor_motion_sensor_enabled" # verify the switch is on before we start assert hass.states.get(test_entity_id).state == "on" # now call the HA turn_off service await hass.services.async_call( "switch", "turn_off", {"entity_id": test_entity_id}, blocking=True, ) # PUT request should have been sent to device with correct params assert len(mock_bridge_v2.mock_requests) == 1 assert mock_bridge_v2.mock_requests[0]["method"] == "put" assert mock_bridge_v2.mock_requests[0]["json"]["enabled"] is False # Now generate update event by emitting the json we've sent as incoming event event = { "id": "b6896534-016d-4052-8cb4-ef04454df62c", "type": "motion", **mock_bridge_v2.mock_requests[0]["json"], } mock_bridge_v2.api.emit_event("update", event) await hass.async_block_till_done() # the switch should now be off test_entity = hass.states.get(test_entity_id) assert test_entity is not None assert test_entity.state == "off" async def test_switch_added(hass: HomeAssistant, mock_bridge_v2: Mock) -> None: """Test new switch added to bridge.""" await mock_bridge_v2.api.load_test_data([FAKE_DEVICE, FAKE_ZIGBEE_CONNECTIVITY]) await setup_platform(hass, mock_bridge_v2, Platform.SWITCH) test_entity_id = "switch.hue_mocked_device_motion_sensor_enabled" # verify entity does not exist before we start assert hass.states.get(test_entity_id) is None # Add new fake entity (and attached device and zigbee_connectivity) by emitting # events mock_bridge_v2.api.emit_event("add", FAKE_BINARY_SENSOR) await hass.async_block_till_done() # the entity should now be available test_entity = hass.states.get(test_entity_id) assert test_entity is not None assert test_entity.state == "on" # test update updated_resource = {**FAKE_BINARY_SENSOR, "enabled": False} mock_bridge_v2.api.emit_event("update", updated_resource) await hass.async_block_till_done() test_entity = hass.states.get(test_entity_id) assert test_entity is not None assert test_entity.state == "off" @pytest.mark.parametrize( "metadata", [ pytest.param( {"name": "Hue Accessories", "category": "accessory"}, id="accessory" ), pytest.param( {"name": "Light state after streaming", "category": "entertainment"}, id="entertainment", ), pytest.param({"name": "Old bridge script"}, id="no_category"), ], ) async def test_internal_behavior_instance_not_added( hass: HomeAssistant, mock_bridge_v2: Mock, v2_resources_test_data: JsonArrayType, metadata: dict, ) -> None: """Test internal behavior instances are not exposed as switches. The bridge accepts a change to `enabled` on these but keeps running them, so a switch for them would silently do nothing. Bridges that do not report a category at all are skipped for the same reason. """ internal_script = {**FAKE_BEHAVIOR_SCRIPT, "metadata": metadata} await mock_bridge_v2.api.load_test_data( [*v2_resources_test_data, internal_script, FAKE_BEHAVIOR_INSTANCE] ) await setup_platform(hass, mock_bridge_v2, Platform.SWITCH) assert hass.states.get("switch.philips_hue_automation_wall_switch_hallway") is None assert hass.states.get("switch.philips_hue_automation_timer_test") is not None assert len(hass.states.async_all()) == 4 async def test_internal_behavior_instance_entity_removed( hass: HomeAssistant, mock_bridge_v2: Mock, v2_resources_test_data: JsonArrayType, entity_registry: er.EntityRegistry, ) -> None: """Test a previously created entity for an internal instance is removed.""" # Simulate an entity created with a previous version of the integration stale_entity = entity_registry.async_get_or_create( Platform.SWITCH, DOMAIN, FAKE_BEHAVIOR_INSTANCE["id"] ) await mock_bridge_v2.api.load_test_data( [*v2_resources_test_data, FAKE_BEHAVIOR_SCRIPT, FAKE_BEHAVIOR_INSTANCE] ) await setup_platform(hass, mock_bridge_v2, Platform.SWITCH) assert entity_registry.async_get(stale_entity.entity_id) is None @pytest.mark.parametrize( ("pm_state", "expected_state"), [("started", "on"), ("stopped", "off")] ) async def test_presence_mimicking_switch_state( hass: HomeAssistant, mock_bridge_v2: Mock, v2_resources_test_data: JsonArrayType, pm_state: str, expected_state: str, ) -> None: """Test the switch follows the run state instead of enabled.""" instance = {**FAKE_PRESENCE_MIMICKING_INSTANCE, "state": {"pm_state": pm_state}} await mock_bridge_v2.api.load_test_data( [*v2_resources_test_data, FAKE_PRESENCE_MIMICKING_SCRIPT, instance] ) await setup_platform(hass, mock_bridge_v2, Platform.SWITCH) test_entity = hass.states.get("switch.philips_hue_automation_mimic_presence") assert test_entity is not None assert test_entity.state == expected_state @pytest.mark.parametrize( ("service", "expected_trigger"), [("turn_on", {"start": {}}), ("turn_off", {"stop": {}})], ) async def test_presence_mimicking_switch_services( hass: HomeAssistant, mock_bridge_v2: Mock, v2_resources_test_data: JsonArrayType, service: str, expected_trigger: dict, ) -> None: """Test the switch starts and stops instead of touching enabled.""" await mock_bridge_v2.api.load_test_data( [ *v2_resources_test_data, FAKE_PRESENCE_MIMICKING_SCRIPT, FAKE_PRESENCE_MIMICKING_INSTANCE, ] ) await setup_platform(hass, mock_bridge_v2, Platform.SWITCH) await hass.services.async_call( "switch", service, {"entity_id": "switch.philips_hue_automation_mimic_presence"}, blocking=True, ) assert len(mock_bridge_v2.mock_requests) == 1 assert mock_bridge_v2.mock_requests[0]["method"] == "put" assert mock_bridge_v2.mock_requests[0]["json"] == {"trigger": expected_trigger} async def test_regular_automation_switch_uses_enabled( hass: HomeAssistant, mock_bridge_v2: Mock, v2_resources_test_data: JsonArrayType ) -> None: """Test an automation without a run state still toggles enabled.""" await mock_bridge_v2.api.load_test_data(v2_resources_test_data) await setup_platform(hass, mock_bridge_v2, Platform.SWITCH) await hass.services.async_call( "switch", "turn_off", {"entity_id": "switch.philips_hue_automation_timer_test"}, blocking=True, ) assert len(mock_bridge_v2.mock_requests) == 1 assert mock_bridge_v2.mock_requests[0]["json"] == {"enabled": False}