From 30243bc35e342ef8a9c2a1be9813accd297157a3 Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Wed, 14 Nov 2018 22:49:01 +1100 Subject: [PATCH 01/12] Add ness alarm control panel using nessclient --- .coveragerc | 3 + CODEOWNERS | 4 + .../alarm_control_panel/ness_alarm.py | 114 +++++++++ .../components/binary_sensor/ness_alarm.py | 88 +++++++ homeassistant/components/ness_alarm.py | 119 +++++++++ requirements_all.txt | 3 + .../alarm_control_panel/test_ness_alarm.py | 69 +++++ .../binary_sensor/test_ness_alarm.py | 36 +++ tests/components/test_ness_alarm.py | 238 ++++++++++++++++++ 9 files changed, 674 insertions(+) create mode 100644 homeassistant/components/alarm_control_panel/ness_alarm.py create mode 100644 homeassistant/components/binary_sensor/ness_alarm.py create mode 100644 homeassistant/components/ness_alarm.py create mode 100644 tests/components/alarm_control_panel/test_ness_alarm.py create mode 100644 tests/components/binary_sensor/test_ness_alarm.py create mode 100644 tests/components/test_ness_alarm.py diff --git a/.coveragerc b/.coveragerc index 7ed4746d1fcaa8..1b62db8bbb7aaf 100644 --- a/.coveragerc +++ b/.coveragerc @@ -251,6 +251,9 @@ omit = homeassistant/components/neato.py homeassistant/components/*/neato.py + homeassistant/components/ness_alarm.py + homeassistant/components/*/ness_alarm.py + homeassistant/components/nest/__init__.py homeassistant/components/*/nest.py diff --git a/CODEOWNERS b/CODEOWNERS index 745f98c09e04f1..018fbed67f0bcf 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -213,6 +213,10 @@ homeassistant/components/melissa.py @kennedyshead homeassistant/components/*/melissa.py @kennedyshead homeassistant/components/*/mystrom.py @fabaff +# N +homeassistant/components/ness_alarm.py @nickw444 +homeassistant/components/*/ness_alarm.py @nickw444 + # O homeassistant/components/openuv/* @bachya homeassistant/components/*/openuv.py @bachya diff --git a/homeassistant/components/alarm_control_panel/ness_alarm.py b/homeassistant/components/alarm_control_panel/ness_alarm.py new file mode 100644 index 00000000000000..7e32df00313498 --- /dev/null +++ b/homeassistant/components/alarm_control_panel/ness_alarm.py @@ -0,0 +1,114 @@ +""" +Support for Ness D8X/D16X alarm panel. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/alarm_control_panel.ness_alarm/ +""" + +import logging + +import homeassistant.components.alarm_control_panel as alarm +from homeassistant.components.ness_alarm import ( + DATA_NESS, SIGNAL_ARMING_STATE_CHANGED) +from homeassistant.const import ( + STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMING, + STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING, STATE_ALARM_DISARMED) +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['ness_alarm'] + + +async def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): + """Set up the Ness Alarm alarm control panel devices.""" + if discovery_info is None: + return + + device = NessAlarmPanel(hass.data[DATA_NESS], 'Alarm Panel') + async_add_entities([device]) + + +class NessAlarmPanel(alarm.AlarmControlPanel): + """Representation of a Ness alarm panel.""" + + def __init__(self, client, name): + """Initialize the alarm panel.""" + self._client = client + self._name = name + self._state = None + self._available = False + + async def async_added_to_hass(self): + """Register callbacks.""" + async_dispatcher_connect( + self.hass, SIGNAL_ARMING_STATE_CHANGED, + self._handle_arming_state_change) + + @property + def available(self): + """Return True if entity is available.""" + return self._available + + @property + def name(self): + """Return the name of the device.""" + return self._name + + @property + def should_poll(self): + """Return the polling state.""" + return False + + @property + def code_format(self): + """Return the regex for code format or None if no code is required.""" + return 'Number' + + @property + def state(self): + """Return the state of the device.""" + return self._state + + async def async_alarm_disarm(self, code=None): + """Send disarm command.""" + await self._client.disarm(code) + + async def async_alarm_arm_away(self, code=None): + """Send arm away command.""" + await self._client.arm_away(code) + + async def async_alarm_arm_home(self, code=None): + """Send arm home command.""" + await self._client.arm_home(code) + + async def async_alarm_trigger(self, code=None): + """Send trigger/panic command.""" + await self._client.panic(code) + + @callback + def _handle_arming_state_change(self, arming_state): + """Handle arming state update.""" + from nessclient import ArmingState + + self._available = True + if arming_state == ArmingState.UNKNOWN: + self._state = None + elif arming_state == ArmingState.DISARMED: + self._state = STATE_ALARM_DISARMED + elif arming_state == ArmingState.ARMING: + self._state = STATE_ALARM_ARMING + elif arming_state == ArmingState.EXIT_DELAY: + self._state = STATE_ALARM_ARMING + elif arming_state == ArmingState.ARMED: + self._state = STATE_ALARM_ARMED_AWAY + elif arming_state == ArmingState.ENTRY_DELAY: + self._state = STATE_ALARM_PENDING + elif arming_state == ArmingState.TRIGGERED: + self._state = STATE_ALARM_TRIGGERED + else: + _LOGGER.warning("Unhandled arming state: %s", arming_state) + + self.async_schedule_update_ha_state() diff --git a/homeassistant/components/binary_sensor/ness_alarm.py b/homeassistant/components/binary_sensor/ness_alarm.py new file mode 100644 index 00000000000000..35690765990024 --- /dev/null +++ b/homeassistant/components/binary_sensor/ness_alarm.py @@ -0,0 +1,88 @@ +""" +Support for Ness D8X/D16X zone states - represented as binary sensors. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.ness_alarm/ +""" +import logging + +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.components.ness_alarm import ( + CONF_ZONES, CONF_ZONE_TYPE, CONF_ZONE_NAME, CONF_ZONE_ID, + SIGNAL_ZONE_CHANGED, ZoneChangedData) +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +DEPENDENCIES = ['ness_alarm'] +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_platform(hass, config, async_add_devices, + discovery_info=None): + """Set up the Ness Alarm binary sensor devices.""" + if not discovery_info: + return + + configured_zones = discovery_info[CONF_ZONES] + + devices = [] + + for zone_config in configured_zones: + zone_type = zone_config[CONF_ZONE_TYPE] + zone_name = zone_config[CONF_ZONE_NAME] + zone_id = zone_config[CONF_ZONE_ID] + device = NessZoneBinarySensor(zone_id=zone_id, name=zone_name, + zone_type=zone_type) + devices.append(device) + + async_add_devices(devices) + + +class NessZoneBinarySensor(BinarySensorDevice): + """Representation of an Ness alarm zone as a binary sensor.""" + + def __init__(self, zone_id, name, zone_type): + """Initialize the binary_sensor.""" + self._zone_id = zone_id + self._name = name + self._type = zone_type + self._state = 0 + self._available = False + + async def async_added_to_hass(self): + """Register callbacks.""" + async_dispatcher_connect( + self.hass, SIGNAL_ZONE_CHANGED, self._handle_zone_change) + + @property + def available(self): + """Return True if entity is available.""" + return self._available + + @property + def name(self): + """Return the name of the entity.""" + return self._name + + @property + def should_poll(self): + """No polling needed.""" + return False + + @property + def is_on(self): + """Return true if sensor is on.""" + return self._state == 1 + + @property + def device_class(self): + """Return the class of this sensor, from DEVICE_CLASSES.""" + return self._type + + @callback + def _handle_zone_change(self, data: ZoneChangedData): + """Handle zone state update.""" + if self._zone_id == data.zone_id: + self._available = True + self._state = data.state + self.async_schedule_update_ha_state() diff --git a/homeassistant/components/ness_alarm.py b/homeassistant/components/ness_alarm.py new file mode 100644 index 00000000000000..5add3532b1796f --- /dev/null +++ b/homeassistant/components/ness_alarm.py @@ -0,0 +1,119 @@ +""" +Support for Ness D8X/D16X devices. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/ness_alarm/ +""" +import logging +from collections import namedtuple + +import voluptuous as vol + +from homeassistant.components.binary_sensor import DEVICE_CLASSES +from homeassistant.const import EVENT_HOMEASSISTANT_STOP +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.discovery import async_load_platform +from homeassistant.helpers.dispatcher import async_dispatcher_send + +REQUIREMENTS = ['nessclient==0.9.6'] + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = 'ness_alarm' +DATA_NESS = 'ness_alarm' + +CONF_DEVICE_HOST = 'host' +CONF_DEVICE_PORT = 'port' +CONF_ZONES = 'zones' +CONF_ZONE_NAME = 'name' +CONF_ZONE_TYPE = 'type' +CONF_ZONE_ID = 'id' +ATTR_CODE = 'code' +ATTR_OUTPUT_ID = 'output_id' +ATTR_STATE = 'state' + +SIGNAL_ZONE_CHANGED = 'ness_alarm.zone_changed' +SIGNAL_ARMING_STATE_CHANGED = 'ness_alarm.arming_state_changed' + +ZoneChangedData = namedtuple('ZoneChangedData', ['zone_id', 'state']) + +DEFAULT_ZONE_TYPE = 'motion' +ZONE_SCHEMA = vol.Schema({ + vol.Required(CONF_ZONE_NAME): cv.string, + vol.Required(CONF_ZONE_ID): cv.positive_int, + vol.Optional(CONF_ZONE_TYPE, default=DEFAULT_ZONE_TYPE): + vol.In(DEVICE_CLASSES)}) + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_DEVICE_HOST): cv.string, + vol.Required(CONF_DEVICE_PORT): cv.port, + vol.Optional(CONF_ZONES): vol.All(cv.ensure_list, [ZONE_SCHEMA]), + }), +}, extra=vol.ALLOW_EXTRA) + +SERVICE_PANIC = 'panic' +SERVICE_AUX = 'aux' + +SERVICE_SCHEMA_PANIC = vol.Schema({ + vol.Required(ATTR_CODE): cv.string, +}) +SERVICE_SCHEMA_AUX = vol.Schema({ + vol.Required(ATTR_OUTPUT_ID): cv.positive_int, + vol.Optional(ATTR_STATE, default=True): cv.boolean, +}) + + +async def async_setup(hass, config): + """Set up the Ness Alarm platform.""" + from nessclient import Client, ArmingState + conf = config[DOMAIN] + + zones = conf.get(CONF_ZONES, []) + host = conf[CONF_DEVICE_HOST] + port = conf[CONF_DEVICE_PORT] + + client = Client(host=host, port=port, loop=hass.loop) + hass.data[DATA_NESS] = client + + async def _close(event): + await client.close() + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _close) + + hass.async_create_task( + async_load_platform(hass, 'binary_sensor', DOMAIN, {CONF_ZONES: zones}, + config)) + hass.async_create_task( + async_load_platform(hass, 'alarm_control_panel', DOMAIN, {}, config)) + + def on_zone_change(zone_id: int, state: bool): + """Receives and propagates zone state updates.""" + async_dispatcher_send(hass, SIGNAL_ZONE_CHANGED, ZoneChangedData( + zone_id=zone_id, + state=state, + )) + + def on_state_change(arming_state: ArmingState): + """Receives and propagates arming state updates.""" + async_dispatcher_send(hass, SIGNAL_ARMING_STATE_CHANGED, arming_state) + + client.on_zone_change(on_zone_change) + client.on_state_change(on_state_change) + + # Force update for current arming status and current zone states + hass.loop.create_task(client.keepalive()) + hass.loop.create_task(client.update()) + + async def handle_panic(call): + await client.panic(call.data[ATTR_CODE]) + + async def handle_aux(call): + await client.aux(call.data[ATTR_OUTPUT_ID], call.data[ATTR_STATE]) + + hass.services.async_register(DOMAIN, 'panic', handle_panic, + schema=SERVICE_SCHEMA_PANIC) + hass.services.async_register(DOMAIN, 'aux', handle_aux, + schema=SERVICE_SCHEMA_AUX) + + return True diff --git a/requirements_all.txt b/requirements_all.txt index 7bb0a0490d2a3b..3dbb3aa54a03c9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -692,6 +692,9 @@ nanoleaf==0.4.1 # homeassistant.components.device_tracker.keenetic_ndms2 ndms2_client==0.0.6 +# homeassistant.components.ness_alarm +nessclient==0.9.5 + # homeassistant.components.sensor.netdata netdata==0.1.2 diff --git a/tests/components/alarm_control_panel/test_ness_alarm.py b/tests/components/alarm_control_panel/test_ness_alarm.py new file mode 100644 index 00000000000000..c63c3581604fb2 --- /dev/null +++ b/tests/components/alarm_control_panel/test_ness_alarm.py @@ -0,0 +1,69 @@ +"""Tests for the ness_alarm alarm control panel component.""" + +from enum import Enum + +import pytest +from asynctest import patch, Mock + +from homeassistant.components.alarm_control_panel.ness_alarm import ( + NessAlarmPanel) +from homeassistant.const import ( + STATE_ALARM_DISARMED, STATE_ALARM_ARMING, + STATE_ALARM_ARMED_AWAY, STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING) +from tests.common import MockDependency + + +async def test_handle_arming_state_change(hass, mock_arming_state): + """Test arming state change handing.""" + states = [ + (MockArmingState.UNKNOWN, None), + (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), + ] + + for arming_state, expected_state in states: + alarm_panel = NessAlarmPanel(client=Mock(), name='Alarm Panel') + alarm_panel.hass = hass + mock_update_ha_state = Mock() + alarm_panel.async_schedule_update_ha_state = mock_update_ha_state + + assert alarm_panel.state is None + alarm_panel._handle_arming_state_change(arming_state) + assert alarm_panel.state == expected_state + assert mock_update_ha_state.call_count == 1 + + +async def test_availability(hass, mock_arming_state): + """Test entity is unavailable until a zone update is handled.""" + alarm_panel = NessAlarmPanel(client=Mock(), name='Alarm Panel') + alarm_panel.hass = hass + mock_update_ha_state = Mock() + alarm_panel.async_schedule_update_ha_state = mock_update_ha_state + + assert alarm_panel.available is False + alarm_panel._handle_arming_state_change(MockArmingState.ARMED) + assert alarm_panel.available is True + + +@pytest.fixture +def mock_arming_state(): + """Mock nessclient ArmingState enum.""" + with MockDependency('nessclient'), \ + patch('nessclient.ArmingState', new=MockArmingState) as mock: + yield mock + + +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' diff --git a/tests/components/binary_sensor/test_ness_alarm.py b/tests/components/binary_sensor/test_ness_alarm.py new file mode 100644 index 00000000000000..6edb67300376c2 --- /dev/null +++ b/tests/components/binary_sensor/test_ness_alarm.py @@ -0,0 +1,36 @@ +"""Tests for the ness_alarm binary sensor component.""" + +from homeassistant.components.binary_sensor.ness_alarm import ( + NessZoneBinarySensor) +from homeassistant.components.ness_alarm import ( + ZoneChangedData) + + +async def test_handle_zone_change(hass): + """Test zone change event handling.""" + sensor = NessZoneBinarySensor(zone_id=1, name='Zone 1', zone_type='motion') + sensor.hass = hass + + assert sensor.is_on is False + sensor._handle_zone_change(ZoneChangedData(zone_id=1, state=True)) + assert sensor.is_on is True + + +async def test_handle_zone_change_different_zone(hass): + """Test zone change event handling for a different zone.""" + sensor = NessZoneBinarySensor(zone_id=1, name='Zone 1', zone_type='motion') + sensor.hass = hass + + assert sensor.is_on is False + sensor._handle_zone_change(ZoneChangedData(zone_id=2, state=True)) + assert sensor.is_on is False + + +async def test_zone_availability(hass): + """Test zone is unavailable until a zone update is handled.""" + sensor = NessZoneBinarySensor(zone_id=1, name='Zone 1', zone_type='motion') + sensor.hass = hass + + assert sensor.available is False + sensor._handle_zone_change(ZoneChangedData(zone_id=1, state=True)) + assert sensor.available is True diff --git a/tests/components/test_ness_alarm.py b/tests/components/test_ness_alarm.py new file mode 100644 index 00000000000000..2b602f327f4439 --- /dev/null +++ b/tests/components/test_ness_alarm.py @@ -0,0 +1,238 @@ +"""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_DEVICE_HOST, 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) +from homeassistant.setup import async_setup_component +from tests.common import MockDependency + +VALID_CONFIG = { + DOMAIN: { + CONF_DEVICE_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 + + mock_nessclient.keepalive.assert_called_once() + mock_nessclient.update.assert_called_once() + + +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() + + on_state_change = mock_nessclient.on_state_change.call_args[0][0] + on_state_change(MockArmingState.DISARMED) + 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() + + on_state_change = mock_nessclient.on_state_change.call_args[0][0] + on_state_change(MockArmingState.DISARMED) + 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() + + on_state_change = mock_nessclient.on_state_change.call_args[0][0] + on_state_change(MockArmingState.DISARMED) + 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() + + on_state_change = mock_nessclient.on_state_change.call_args[0][0] + on_state_change(MockArmingState.DISARMED) + 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') + + +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 From 0407bccbea95699aa94871f6773ec56c41df5d73 Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Fri, 28 Dec 2018 09:48:25 +1100 Subject: [PATCH 02/12] indenting --- tests/components/test_ness_alarm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/test_ness_alarm.py b/tests/components/test_ness_alarm.py index 2b602f327f4439..8b17e1658c1a2c 100644 --- a/tests/components/test_ness_alarm.py +++ b/tests/components/test_ness_alarm.py @@ -233,6 +233,6 @@ def mock_nessclient(): _mock_factory.return_value = _mock_instance with MockDependency('nessclient'), \ - patch('nessclient.Client', new=_mock_factory, create=True), \ - patch('nessclient.ArmingState', new=MockArmingState): + patch('nessclient.Client', new=_mock_factory, create=True), \ + patch('nessclient.ArmingState', new=MockArmingState): yield _mock_instance From 784e9465268aaa10a8cc9c3ecfc2c488fbea13c5 Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Fri, 28 Dec 2018 10:22:19 +1100 Subject: [PATCH 03/12] . --- requirements_all.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_all.txt b/requirements_all.txt index 3dbb3aa54a03c9..bedbbd14f50937 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -693,7 +693,7 @@ nanoleaf==0.4.1 ndms2_client==0.0.6 # homeassistant.components.ness_alarm -nessclient==0.9.5 +nessclient==0.9.6 # homeassistant.components.sensor.netdata netdata==0.1.2 From 47289d96309a962f8e1eb2aa1a5c5d124e171681 Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Fri, 28 Dec 2018 10:26:35 +1100 Subject: [PATCH 04/12] Remove availability functionality, will improve and add back in another PR --- .../components/alarm_control_panel/ness_alarm.py | 7 ------- .../components/binary_sensor/ness_alarm.py | 7 ------- .../alarm_control_panel/test_ness_alarm.py | 12 ------------ .../components/binary_sensor/test_ness_alarm.py | 10 ---------- tests/components/test_ness_alarm.py | 16 ---------------- 5 files changed, 52 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/ness_alarm.py b/homeassistant/components/alarm_control_panel/ness_alarm.py index 7e32df00313498..ec52ef51e2f0b3 100644 --- a/homeassistant/components/alarm_control_panel/ness_alarm.py +++ b/homeassistant/components/alarm_control_panel/ness_alarm.py @@ -39,7 +39,6 @@ def __init__(self, client, name): self._client = client self._name = name self._state = None - self._available = False async def async_added_to_hass(self): """Register callbacks.""" @@ -47,11 +46,6 @@ async def async_added_to_hass(self): self.hass, SIGNAL_ARMING_STATE_CHANGED, self._handle_arming_state_change) - @property - def available(self): - """Return True if entity is available.""" - return self._available - @property def name(self): """Return the name of the device.""" @@ -93,7 +87,6 @@ def _handle_arming_state_change(self, arming_state): """Handle arming state update.""" from nessclient import ArmingState - self._available = True if arming_state == ArmingState.UNKNOWN: self._state = None elif arming_state == ArmingState.DISARMED: diff --git a/homeassistant/components/binary_sensor/ness_alarm.py b/homeassistant/components/binary_sensor/ness_alarm.py index 35690765990024..97552ec427e6b3 100644 --- a/homeassistant/components/binary_sensor/ness_alarm.py +++ b/homeassistant/components/binary_sensor/ness_alarm.py @@ -47,18 +47,12 @@ def __init__(self, zone_id, name, zone_type): self._name = name self._type = zone_type self._state = 0 - self._available = False async def async_added_to_hass(self): """Register callbacks.""" async_dispatcher_connect( self.hass, SIGNAL_ZONE_CHANGED, self._handle_zone_change) - @property - def available(self): - """Return True if entity is available.""" - return self._available - @property def name(self): """Return the name of the entity.""" @@ -83,6 +77,5 @@ def device_class(self): def _handle_zone_change(self, data: ZoneChangedData): """Handle zone state update.""" if self._zone_id == data.zone_id: - self._available = True self._state = data.state self.async_schedule_update_ha_state() diff --git a/tests/components/alarm_control_panel/test_ness_alarm.py b/tests/components/alarm_control_panel/test_ness_alarm.py index c63c3581604fb2..18f9c3bfc3f071 100644 --- a/tests/components/alarm_control_panel/test_ness_alarm.py +++ b/tests/components/alarm_control_panel/test_ness_alarm.py @@ -37,18 +37,6 @@ async def test_handle_arming_state_change(hass, mock_arming_state): assert mock_update_ha_state.call_count == 1 -async def test_availability(hass, mock_arming_state): - """Test entity is unavailable until a zone update is handled.""" - alarm_panel = NessAlarmPanel(client=Mock(), name='Alarm Panel') - alarm_panel.hass = hass - mock_update_ha_state = Mock() - alarm_panel.async_schedule_update_ha_state = mock_update_ha_state - - assert alarm_panel.available is False - alarm_panel._handle_arming_state_change(MockArmingState.ARMED) - assert alarm_panel.available is True - - @pytest.fixture def mock_arming_state(): """Mock nessclient ArmingState enum.""" diff --git a/tests/components/binary_sensor/test_ness_alarm.py b/tests/components/binary_sensor/test_ness_alarm.py index 6edb67300376c2..f0e24a842783de 100644 --- a/tests/components/binary_sensor/test_ness_alarm.py +++ b/tests/components/binary_sensor/test_ness_alarm.py @@ -24,13 +24,3 @@ async def test_handle_zone_change_different_zone(hass): assert sensor.is_on is False sensor._handle_zone_change(ZoneChangedData(zone_id=2, state=True)) assert sensor.is_on is False - - -async def test_zone_availability(hass): - """Test zone is unavailable until a zone update is handled.""" - sensor = NessZoneBinarySensor(zone_id=1, name='Zone 1', zone_type='motion') - sensor.hass = hass - - assert sensor.available is False - sensor._handle_zone_change(ZoneChangedData(zone_id=1, state=True)) - assert sensor.available is True diff --git a/tests/components/test_ness_alarm.py b/tests/components/test_ness_alarm.py index 8b17e1658c1a2c..01a9d902072caa 100644 --- a/tests/components/test_ness_alarm.py +++ b/tests/components/test_ness_alarm.py @@ -86,10 +86,6 @@ async def test_alarm_disarm(hass, mock_nessclient): 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.DISARMED) - await hass.async_block_till_done() - await hass.services.async_call( alarm_control_panel.DOMAIN, SERVICE_ALARM_DISARM, blocking=True, service_data={ @@ -104,10 +100,6 @@ async def test_alarm_arm_away(hass, mock_nessclient): 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.DISARMED) - await hass.async_block_till_done() - await hass.services.async_call( alarm_control_panel.DOMAIN, SERVICE_ALARM_ARM_AWAY, blocking=True, service_data={ @@ -122,10 +114,6 @@ async def test_alarm_arm_home(hass, mock_nessclient): 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.DISARMED) - await hass.async_block_till_done() - await hass.services.async_call( alarm_control_panel.DOMAIN, SERVICE_ALARM_ARM_HOME, blocking=True, service_data={ @@ -140,10 +128,6 @@ async def test_alarm_trigger(hass, mock_nessclient): 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.DISARMED) - await hass.async_block_till_done() - await hass.services.async_call( alarm_control_panel.DOMAIN, SERVICE_ALARM_TRIGGER, blocking=True, service_data={ From 26537a378cb66b7fd84b030ced67afeb91f3bef3 Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Fri, 28 Dec 2018 11:17:52 +1100 Subject: [PATCH 05/12] Use call_count --- tests/components/test_ness_alarm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/test_ness_alarm.py b/tests/components/test_ness_alarm.py index 01a9d902072caa..854a4cd97570aa 100644 --- a/tests/components/test_ness_alarm.py +++ b/tests/components/test_ness_alarm.py @@ -44,8 +44,8 @@ async def test_setup_platform(hass, mock_nessclient): assert hass.states.get('binary_sensor.zone_1') is not None assert hass.states.get('binary_sensor.zone_2') is not None - mock_nessclient.keepalive.assert_called_once() - mock_nessclient.update.assert_called_once() + assert mock_nessclient.keepalive.call_count == 1 + assert mock_nessclient.update.call_count == 1 async def test_panic_service(hass, mock_nessclient): From 0058a2bbac8b83b942d448a4ca10d8884b8d99f9 Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Fri, 28 Dec 2018 11:19:04 +1100 Subject: [PATCH 06/12] lint --- tests/components/alarm_control_panel/test_ness_alarm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/alarm_control_panel/test_ness_alarm.py b/tests/components/alarm_control_panel/test_ness_alarm.py index 18f9c3bfc3f071..b5d41a96e0e874 100644 --- a/tests/components/alarm_control_panel/test_ness_alarm.py +++ b/tests/components/alarm_control_panel/test_ness_alarm.py @@ -41,7 +41,7 @@ async def test_handle_arming_state_change(hass, mock_arming_state): def mock_arming_state(): """Mock nessclient ArmingState enum.""" with MockDependency('nessclient'), \ - patch('nessclient.ArmingState', new=MockArmingState) as mock: + patch('nessclient.ArmingState', new=MockArmingState) as mock: yield mock From d326442a8a847f0ad87b41969973628ac138e7dd Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Fri, 28 Dec 2018 11:20:42 +1100 Subject: [PATCH 07/12] lint --- tests/components/alarm_control_panel/test_ness_alarm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/alarm_control_panel/test_ness_alarm.py b/tests/components/alarm_control_panel/test_ness_alarm.py index b5d41a96e0e874..9c7d9942115611 100644 --- a/tests/components/alarm_control_panel/test_ness_alarm.py +++ b/tests/components/alarm_control_panel/test_ness_alarm.py @@ -41,7 +41,7 @@ async def test_handle_arming_state_change(hass, mock_arming_state): def mock_arming_state(): """Mock nessclient ArmingState enum.""" with MockDependency('nessclient'), \ - patch('nessclient.ArmingState', new=MockArmingState) as mock: + patch('nessclient.ArmingState', new=MockArmingState) as mock: yield mock From 3c0a8530e60df782fca1beead9b93817d3805dfe Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Mon, 31 Dec 2018 11:07:24 +1100 Subject: [PATCH 08/12] Review changes --- .../components/binary_sensor/ness_alarm.py | 4 +- homeassistant/components/ness_alarm.py | 10 ++-- .../alarm_control_panel/test_ness_alarm.py | 57 ------------------- .../binary_sensor/test_ness_alarm.py | 26 --------- tests/components/test_ness_alarm.py | 33 ++++++++++- 5 files changed, 38 insertions(+), 92 deletions(-) delete mode 100644 tests/components/alarm_control_panel/test_ness_alarm.py delete mode 100644 tests/components/binary_sensor/test_ness_alarm.py diff --git a/homeassistant/components/binary_sensor/ness_alarm.py b/homeassistant/components/binary_sensor/ness_alarm.py index 97552ec427e6b3..9f1479efd6983f 100644 --- a/homeassistant/components/binary_sensor/ness_alarm.py +++ b/homeassistant/components/binary_sensor/ness_alarm.py @@ -17,7 +17,7 @@ _LOGGER = logging.getLogger(__name__) -async def async_setup_platform(hass, config, async_add_devices, +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the Ness Alarm binary sensor devices.""" if not discovery_info: @@ -35,7 +35,7 @@ async def async_setup_platform(hass, config, async_add_devices, zone_type=zone_type) devices.append(device) - async_add_devices(devices) + async_add_entities(devices) class NessZoneBinarySensor(BinarySensorDevice): diff --git a/homeassistant/components/ness_alarm.py b/homeassistant/components/ness_alarm.py index 5add3532b1796f..def06c4769e1af 100644 --- a/homeassistant/components/ness_alarm.py +++ b/homeassistant/components/ness_alarm.py @@ -31,6 +31,7 @@ ATTR_CODE = 'code' ATTR_OUTPUT_ID = 'output_id' ATTR_STATE = 'state' +DEFAULT_ZONES = [] SIGNAL_ZONE_CHANGED = 'ness_alarm.zone_changed' SIGNAL_ARMING_STATE_CHANGED = 'ness_alarm.arming_state_changed' @@ -48,7 +49,8 @@ DOMAIN: vol.Schema({ vol.Required(CONF_DEVICE_HOST): cv.string, vol.Required(CONF_DEVICE_PORT): cv.port, - vol.Optional(CONF_ZONES): vol.All(cv.ensure_list, [ZONE_SCHEMA]), + vol.Optional(CONF_ZONES, default=DEFAULT_ZONES): + vol.All(cv.ensure_list, [ZONE_SCHEMA]), }), }, extra=vol.ALLOW_EXTRA) @@ -69,7 +71,7 @@ async def async_setup(hass, config): from nessclient import Client, ArmingState conf = config[DOMAIN] - zones = conf.get(CONF_ZONES, []) + zones = conf[CONF_ZONES] host = conf[CONF_DEVICE_HOST] port = conf[CONF_DEVICE_PORT] @@ -111,9 +113,9 @@ async def handle_panic(call): async def handle_aux(call): await client.aux(call.data[ATTR_OUTPUT_ID], call.data[ATTR_STATE]) - hass.services.async_register(DOMAIN, 'panic', handle_panic, + hass.services.async_register(DOMAIN, SERVICE_PANIC, handle_panic, schema=SERVICE_SCHEMA_PANIC) - hass.services.async_register(DOMAIN, 'aux', handle_aux, + hass.services.async_register(DOMAIN, SERVICE_AUX, handle_aux, schema=SERVICE_SCHEMA_AUX) return True diff --git a/tests/components/alarm_control_panel/test_ness_alarm.py b/tests/components/alarm_control_panel/test_ness_alarm.py deleted file mode 100644 index 9c7d9942115611..00000000000000 --- a/tests/components/alarm_control_panel/test_ness_alarm.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Tests for the ness_alarm alarm control panel component.""" - -from enum import Enum - -import pytest -from asynctest import patch, Mock - -from homeassistant.components.alarm_control_panel.ness_alarm import ( - NessAlarmPanel) -from homeassistant.const import ( - STATE_ALARM_DISARMED, STATE_ALARM_ARMING, - STATE_ALARM_ARMED_AWAY, STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING) -from tests.common import MockDependency - - -async def test_handle_arming_state_change(hass, mock_arming_state): - """Test arming state change handing.""" - states = [ - (MockArmingState.UNKNOWN, None), - (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), - ] - - for arming_state, expected_state in states: - alarm_panel = NessAlarmPanel(client=Mock(), name='Alarm Panel') - alarm_panel.hass = hass - mock_update_ha_state = Mock() - alarm_panel.async_schedule_update_ha_state = mock_update_ha_state - - assert alarm_panel.state is None - alarm_panel._handle_arming_state_change(arming_state) - assert alarm_panel.state == expected_state - assert mock_update_ha_state.call_count == 1 - - -@pytest.fixture -def mock_arming_state(): - """Mock nessclient ArmingState enum.""" - with MockDependency('nessclient'), \ - patch('nessclient.ArmingState', new=MockArmingState) as mock: - yield mock - - -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' diff --git a/tests/components/binary_sensor/test_ness_alarm.py b/tests/components/binary_sensor/test_ness_alarm.py deleted file mode 100644 index f0e24a842783de..00000000000000 --- a/tests/components/binary_sensor/test_ness_alarm.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Tests for the ness_alarm binary sensor component.""" - -from homeassistant.components.binary_sensor.ness_alarm import ( - NessZoneBinarySensor) -from homeassistant.components.ness_alarm import ( - ZoneChangedData) - - -async def test_handle_zone_change(hass): - """Test zone change event handling.""" - sensor = NessZoneBinarySensor(zone_id=1, name='Zone 1', zone_type='motion') - sensor.hass = hass - - assert sensor.is_on is False - sensor._handle_zone_change(ZoneChangedData(zone_id=1, state=True)) - assert sensor.is_on is True - - -async def test_handle_zone_change_different_zone(hass): - """Test zone change event handling for a different zone.""" - sensor = NessZoneBinarySensor(zone_id=1, name='Zone 1', zone_type='motion') - sensor.hass = hass - - assert sensor.is_on is False - sensor._handle_zone_change(ZoneChangedData(zone_id=2, state=True)) - assert sensor.is_on is False diff --git a/tests/components/test_ness_alarm.py b/tests/components/test_ness_alarm.py index 854a4cd97570aa..b5b2df82b912ee 100644 --- a/tests/components/test_ness_alarm.py +++ b/tests/components/test_ness_alarm.py @@ -9,9 +9,11 @@ DOMAIN, CONF_DEVICE_PORT, CONF_DEVICE_HOST, 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) +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) from homeassistant.setup import async_setup_component from tests.common import MockDependency @@ -147,6 +149,31 @@ async def test_dispatch_zone_change(hass, mock_nessclient): 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): From a93ae23d72b5b8affc03bb13f6c4f5aa079480d1 Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Mon, 31 Dec 2018 11:08:05 +1100 Subject: [PATCH 09/12] Lint --- tests/components/test_ness_alarm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/components/test_ness_alarm.py b/tests/components/test_ness_alarm.py index b5b2df82b912ee..7d2104c9306c67 100644 --- a/tests/components/test_ness_alarm.py +++ b/tests/components/test_ness_alarm.py @@ -166,7 +166,8 @@ async def test_arming_state_change(hass, mock_nessclient): 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) + 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: From f8ea58bf8e3a807108b6f89593db9b538f435560 Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Mon, 31 Dec 2018 11:08:56 +1100 Subject: [PATCH 10/12] Bump nessclient to 0.9.8 --- homeassistant/components/ness_alarm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/ness_alarm.py b/homeassistant/components/ness_alarm.py index def06c4769e1af..eebf8049021b9f 100644 --- a/homeassistant/components/ness_alarm.py +++ b/homeassistant/components/ness_alarm.py @@ -15,7 +15,7 @@ from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send -REQUIREMENTS = ['nessclient==0.9.6'] +REQUIREMENTS = ['nessclient==0.9.8'] _LOGGER = logging.getLogger(__name__) From a6933ff99484f9820b345b5dfb379e053e5e143f Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Mon, 31 Dec 2018 11:56:20 +1100 Subject: [PATCH 11/12] Bump nessclient to 0.9.9 --- homeassistant/components/ness_alarm.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/ness_alarm.py b/homeassistant/components/ness_alarm.py index eebf8049021b9f..e97ee903abccf8 100644 --- a/homeassistant/components/ness_alarm.py +++ b/homeassistant/components/ness_alarm.py @@ -15,7 +15,7 @@ from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send -REQUIREMENTS = ['nessclient==0.9.8'] +REQUIREMENTS = ['nessclient==0.9.9'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index bedbbd14f50937..b1662557abbe6b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -693,7 +693,7 @@ nanoleaf==0.4.1 ndms2_client==0.0.6 # homeassistant.components.ness_alarm -nessclient==0.9.6 +nessclient==0.9.9 # homeassistant.components.sensor.netdata netdata==0.1.2 From 5cb55c1533d2dd12add8bf2ea4a9d48ef269adf1 Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Tue, 1 Jan 2019 17:49:22 +1100 Subject: [PATCH 12/12] Remove from .coveragerc --- .coveragerc | 3 --- 1 file changed, 3 deletions(-) diff --git a/.coveragerc b/.coveragerc index 1b62db8bbb7aaf..7ed4746d1fcaa8 100644 --- a/.coveragerc +++ b/.coveragerc @@ -251,9 +251,6 @@ omit = homeassistant/components/neato.py homeassistant/components/*/neato.py - homeassistant/components/ness_alarm.py - homeassistant/components/*/ness_alarm.py - homeassistant/components/nest/__init__.py homeassistant/components/*/nest.py