"""Tests for the ness_alarm component.""" from enum import Enum import pytest from asynctest import patch, MagicMock from homeassistant.components import alarm_control_panel from homeassistant.components.ness_alarm import ( DOMAIN, CONF_DEVICE_PORT, CONF_ZONE_NAME, CONF_ZONES, CONF_ZONE_ID, SERVICE_AUX, SERVICE_PANIC, ATTR_CODE, ATTR_OUTPUT_ID) from homeassistant.const import ( STATE_ALARM_ARMING, SERVICE_ALARM_DISARM, ATTR_ENTITY_ID, SERVICE_ALARM_ARM_AWAY, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_TRIGGER, STATE_ALARM_DISARMED, STATE_ALARM_ARMED_AWAY, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, STATE_UNKNOWN, CONF_HOST) from homeassistant.setup import async_setup_component from tests.common import MockDependency VALID_CONFIG = { DOMAIN: { CONF_HOST: 'alarm.local', CONF_DEVICE_PORT: 1234, CONF_ZONES: [ { CONF_ZONE_NAME: 'Zone 1', CONF_ZONE_ID: 1, }, { CONF_ZONE_NAME: 'Zone 2', CONF_ZONE_ID: 2, } ] } } async def test_setup_platform(hass, mock_nessclient): """Test platform setup.""" await async_setup_component(hass, DOMAIN, VALID_CONFIG) assert hass.services.has_service(DOMAIN, 'panic') assert hass.services.has_service(DOMAIN, 'aux') await hass.async_block_till_done() assert hass.states.get('alarm_control_panel.alarm_panel') is not None assert hass.states.get('binary_sensor.zone_1') is not None assert hass.states.get('binary_sensor.zone_2') is not None assert mock_nessclient.keepalive.call_count == 1 assert mock_nessclient.update.call_count == 1 async def test_panic_service(hass, mock_nessclient): """Test calling panic service.""" await async_setup_component(hass, DOMAIN, VALID_CONFIG) await hass.services.async_call( DOMAIN, SERVICE_PANIC, blocking=True, service_data={ ATTR_CODE: '1234' }) mock_nessclient.panic.assert_awaited_once_with('1234') async def test_aux_service(hass, mock_nessclient): """Test calling aux service.""" await async_setup_component(hass, DOMAIN, VALID_CONFIG) await hass.services.async_call( DOMAIN, SERVICE_AUX, blocking=True, service_data={ ATTR_OUTPUT_ID: 1 }) mock_nessclient.aux.assert_awaited_once_with(1, True) async def test_dispatch_state_change(hass, mock_nessclient): """Test calling aux service.""" await async_setup_component(hass, DOMAIN, VALID_CONFIG) await hass.async_block_till_done() on_state_change = mock_nessclient.on_state_change.call_args[0][0] on_state_change(MockArmingState.ARMING) await hass.async_block_till_done() assert hass.states.is_state('alarm_control_panel.alarm_panel', STATE_ALARM_ARMING) async def test_alarm_disarm(hass, mock_nessclient): """Test disarm.""" await async_setup_component(hass, DOMAIN, VALID_CONFIG) await hass.async_block_till_done() await hass.services.async_call( alarm_control_panel.DOMAIN, SERVICE_ALARM_DISARM, blocking=True, service_data={ ATTR_ENTITY_ID: 'alarm_control_panel.alarm_panel', ATTR_CODE: '1234' }) mock_nessclient.disarm.assert_called_once_with('1234') async def test_alarm_arm_away(hass, mock_nessclient): """Test disarm.""" await async_setup_component(hass, DOMAIN, VALID_CONFIG) await hass.async_block_till_done() await hass.services.async_call( alarm_control_panel.DOMAIN, SERVICE_ALARM_ARM_AWAY, blocking=True, service_data={ ATTR_ENTITY_ID: 'alarm_control_panel.alarm_panel', ATTR_CODE: '1234' }) mock_nessclient.arm_away.assert_called_once_with('1234') async def test_alarm_arm_home(hass, mock_nessclient): """Test disarm.""" await async_setup_component(hass, DOMAIN, VALID_CONFIG) await hass.async_block_till_done() await hass.services.async_call( alarm_control_panel.DOMAIN, SERVICE_ALARM_ARM_HOME, blocking=True, service_data={ ATTR_ENTITY_ID: 'alarm_control_panel.alarm_panel', ATTR_CODE: '1234' }) mock_nessclient.arm_home.assert_called_once_with('1234') async def test_alarm_trigger(hass, mock_nessclient): """Test disarm.""" await async_setup_component(hass, DOMAIN, VALID_CONFIG) await hass.async_block_till_done() await hass.services.async_call( alarm_control_panel.DOMAIN, SERVICE_ALARM_TRIGGER, blocking=True, service_data={ ATTR_ENTITY_ID: 'alarm_control_panel.alarm_panel', ATTR_CODE: '1234' }) mock_nessclient.panic.assert_called_once_with('1234') async def test_dispatch_zone_change(hass, mock_nessclient): """Test zone change events dispatch a signal to subscribers.""" await async_setup_component(hass, DOMAIN, VALID_CONFIG) await hass.async_block_till_done() on_zone_change = mock_nessclient.on_zone_change.call_args[0][0] on_zone_change(1, True) await hass.async_block_till_done() assert hass.states.is_state('binary_sensor.zone_1', 'on') assert hass.states.is_state('binary_sensor.zone_2', 'off') async def test_arming_state_change(hass, mock_nessclient): """Test arming state change handing.""" states = [ (MockArmingState.UNKNOWN, STATE_UNKNOWN), (MockArmingState.DISARMED, STATE_ALARM_DISARMED), (MockArmingState.ARMING, STATE_ALARM_ARMING), (MockArmingState.EXIT_DELAY, STATE_ALARM_ARMING), (MockArmingState.ARMED, STATE_ALARM_ARMED_AWAY), (MockArmingState.ENTRY_DELAY, STATE_ALARM_PENDING), (MockArmingState.TRIGGERED, STATE_ALARM_TRIGGERED), ] await async_setup_component(hass, DOMAIN, VALID_CONFIG) await hass.async_block_till_done() assert hass.states.is_state('alarm_control_panel.alarm_panel', STATE_UNKNOWN) on_state_change = mock_nessclient.on_state_change.call_args[0][0] for arming_state, expected_state in states: on_state_change(arming_state) await hass.async_block_till_done() assert hass.states.is_state('alarm_control_panel.alarm_panel', expected_state) class MockArmingState(Enum): """Mock nessclient.ArmingState enum.""" UNKNOWN = 'UNKNOWN' DISARMED = 'DISARMED' ARMING = 'ARMING' EXIT_DELAY = 'EXIT_DELAY' ARMED = 'ARMED' ENTRY_DELAY = 'ENTRY_DELAY' TRIGGERED = 'TRIGGERED' class MockClient: """Mock nessclient.Client stub.""" async def panic(self, code): """Handle panic.""" pass async def disarm(self, code): """Handle disarm.""" pass async def arm_away(self, code): """Handle arm_away.""" pass async def arm_home(self, code): """Handle arm_home.""" pass async def aux(self, output_id, state): """Handle auxiliary control.""" pass async def keepalive(self): """Handle keepalive.""" pass async def update(self): """Handle update.""" pass def on_zone_change(self): """Handle on_zone_change.""" pass def on_state_change(self): """Handle on_state_change.""" pass async def close(self): """Handle close.""" pass @pytest.fixture def mock_nessclient(): """Mock the nessclient Client constructor. Replaces nessclient.Client with a Mock which always returns the same MagicMock() instance. """ _mock_instance = MagicMock(MockClient()) _mock_factory = MagicMock() _mock_factory.return_value = _mock_instance with MockDependency('nessclient'), \ patch('nessclient.Client', new=_mock_factory, create=True), \ patch('nessclient.ArmingState', new=MockArmingState): yield _mock_instance