Merge pull request #1308 from kk7ds/binary-sensor-class

Add binary sensor class
This commit is contained in:
Paulus Schoutsen 2016-02-18 21:09:17 -08:00
commit 233a2a2878
5 changed files with 93 additions and 12 deletions

View File

@ -17,6 +17,17 @@ DOMAIN = 'binary_sensor'
SCAN_INTERVAL = 30 SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}' ENTITY_ID_FORMAT = DOMAIN + '.{}'
SENSOR_CLASSES = [
None, # Generic on/off
'opening', # Door, window, etc
'motion', # Motion sensor
'gas', # CO, CO2, etc
'smoke', # Smoke detector
'moisture', # Specifically a wetness sensor
'light', # Lightness threshold
'power', # Power, over-current, etc
'safety', # Generic on=unsafe, off=safe
]
def setup(hass, config): def setup(hass, config):
@ -47,3 +58,14 @@ class BinarySensorDevice(Entity):
def friendly_state(self): def friendly_state(self):
""" Returns the friendly state of the binary sensor. """ """ Returns the friendly state of the binary sensor. """
return None return None
@property
def sensor_class(self):
""" Returns the class of this sensor, from SENSOR_CASSES. """
return None
@property
def device_state_attributes(self):
return {
'sensor_class': self.sensor_class,
}

View File

@ -9,17 +9,23 @@ from homeassistant.components.binary_sensor import BinarySensorDevice
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo binary sensors. """ """ Sets up the Demo binary sensors. """
add_devices([ add_devices([
DemoBinarySensor('Basement Floor Wet', False), DemoBinarySensor('Basement Floor Wet', False, 'moisture'),
DemoBinarySensor('Movement Backyard', True), DemoBinarySensor('Movement Backyard', True, 'motion'),
]) ])
class DemoBinarySensor(BinarySensorDevice): class DemoBinarySensor(BinarySensorDevice):
""" A Demo binary sensor. """ """ A Demo binary sensor. """
def __init__(self, name, state): def __init__(self, name, state, sensor_class):
self._name = name self._name = name
self._state = state self._state = state
self._sensor_type = sensor_class
@property
def sensor_class(self):
""" Return our class. """
return self._sensor_type
@property @property
def should_poll(self): def should_poll(self):

View File

@ -12,7 +12,8 @@ import time
import requests import requests
from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.binary_sensor import (BinarySensorDevice,
SENSOR_CLASSES)
REQUIREMENTS = ['pynx584==0.2'] REQUIREMENTS = ['pynx584==0.2']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -24,11 +25,17 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
host = config.get('host', 'localhost:5007') host = config.get('host', 'localhost:5007')
exclude = config.get('exclude_zones', []) exclude = config.get('exclude_zones', [])
zone_types = config.get('zone_types', {})
if not all(isinstance(zone, int) for zone in exclude): if not all(isinstance(zone, int) for zone in exclude):
_LOGGER.error('Invalid excluded zone specified (use zone number)') _LOGGER.error('Invalid excluded zone specified (use zone number)')
return False return False
if not all(isinstance(zone, int) and ztype in SENSOR_CLASSES
for zone, ztype in zone_types.items()):
_LOGGER.error('Invalid zone_types entry')
return False
try: try:
client = nx584_client.Client('http://%s' % host) client = nx584_client.Client('http://%s' % host)
zones = client.list_zones() zones = client.list_zones()
@ -42,7 +49,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
return False return False
zone_sensors = { zone_sensors = {
zone['number']: NX584ZoneSensor(zone) zone['number']: NX584ZoneSensor(
zone,
zone_types.get(zone['number'], 'opening'))
for zone in zones for zone in zones
if zone['number'] not in exclude} if zone['number'] not in exclude}
if zone_sensors: if zone_sensors:
@ -58,8 +67,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class NX584ZoneSensor(BinarySensorDevice): class NX584ZoneSensor(BinarySensorDevice):
"""Represents a NX584 zone as a sensor.""" """Represents a NX584 zone as a sensor."""
def __init__(self, zone): def __init__(self, zone, zone_type):
self._zone = zone self._zone = zone
self._zone_type = zone_type
@property
def sensor_class(self):
return self._zone_type
@property @property
def should_poll(self): def should_poll(self):

View File

@ -0,0 +1,36 @@
"""
tests.components.binary_sensor.test_binary_sensor
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Test the binary_sensor base class
"""
import unittest
from unittest import mock
from homeassistant.components import binary_sensor
from homeassistant.const import STATE_ON, STATE_OFF
class TestBinarySensor(unittest.TestCase):
def test_state(self):
sensor = binary_sensor.BinarySensorDevice()
self.assertEqual(STATE_OFF, sensor.state)
with mock.patch('homeassistant.components.binary_sensor.'
'BinarySensorDevice.is_on',
new=False):
self.assertEqual(STATE_OFF,
binary_sensor.BinarySensorDevice().state)
with mock.patch('homeassistant.components.binary_sensor.'
'BinarySensorDevice.is_on',
new=True):
self.assertEqual(STATE_ON,
binary_sensor.BinarySensorDevice().state)
def test_attributes(self):
sensor = binary_sensor.BinarySensorDevice()
self.assertEqual({'sensor_class': None},
sensor.device_state_attributes)
with mock.patch('homeassistant.components.binary_sensor.'
'BinarySensorDevice.sensor_class',
new='motion'):
self.assertEqual({'sensor_class': 'motion'},
sensor.device_state_attributes)

View File

@ -41,7 +41,7 @@ class TestNX584SensorSetup(unittest.TestCase):
hass = mock.MagicMock() hass = mock.MagicMock()
self.assertTrue(nx584.setup_platform(hass, {}, add_devices)) self.assertTrue(nx584.setup_platform(hass, {}, add_devices))
mock_nx.assert_has_calls([ mock_nx.assert_has_calls([
mock.call(zone) mock.call(zone, 'opening')
for zone in self.fake_zones]) for zone in self.fake_zones])
self.assertTrue(add_devices.called) self.assertTrue(add_devices.called)
nx584_client.Client.assert_called_once_with('http://localhost:5007') nx584_client.Client.assert_called_once_with('http://localhost:5007')
@ -58,8 +58,8 @@ class TestNX584SensorSetup(unittest.TestCase):
hass = mock.MagicMock() hass = mock.MagicMock()
self.assertTrue(nx584.setup_platform(hass, config, add_devices)) self.assertTrue(nx584.setup_platform(hass, config, add_devices))
mock_nx.assert_has_calls([ mock_nx.assert_has_calls([
mock.call(self.fake_zones[0]), mock.call(self.fake_zones[0], 'opening'),
mock.call(self.fake_zones[2]), mock.call(self.fake_zones[2], 'motion'),
]) ])
self.assertTrue(add_devices.called) self.assertTrue(add_devices.called)
nx584_client.Client.assert_called_once_with('http://foo:123') nx584_client.Client.assert_called_once_with('http://foo:123')
@ -74,6 +74,9 @@ class TestNX584SensorSetup(unittest.TestCase):
def test_setup_bad_config(self): def test_setup_bad_config(self):
bad_configs = [ bad_configs = [
{'exclude_zones': ['a']}, {'exclude_zones': ['a']},
{'zone_types': {'a': 'b'}},
{'zone_types': {1: 'notatype'}},
{'zone_types': {'notazone': 'motion'}},
] ]
for config in bad_configs: for config in bad_configs:
self._test_assert_graceful_fail(config) self._test_assert_graceful_fail(config)
@ -98,7 +101,7 @@ class TestNX584SensorSetup(unittest.TestCase):
class TestNX584ZoneSensor(unittest.TestCase): class TestNX584ZoneSensor(unittest.TestCase):
def test_sensor_normal(self): def test_sensor_normal(self):
zone = {'number': 1, 'name': 'foo', 'state': True} zone = {'number': 1, 'name': 'foo', 'state': True}
sensor = nx584.NX584ZoneSensor(zone) sensor = nx584.NX584ZoneSensor(zone, 'motion')
self.assertEqual('foo', sensor.name) self.assertEqual('foo', sensor.name)
self.assertFalse(sensor.should_poll) self.assertFalse(sensor.should_poll)
self.assertTrue(sensor.is_on) self.assertTrue(sensor.is_on)
@ -113,8 +116,8 @@ class TestNX584Watcher(unittest.TestCase):
zone1 = {'number': 1, 'name': 'foo', 'state': True} zone1 = {'number': 1, 'name': 'foo', 'state': True}
zone2 = {'number': 2, 'name': 'bar', 'state': True} zone2 = {'number': 2, 'name': 'bar', 'state': True}
zones = { zones = {
1: nx584.NX584ZoneSensor(zone1), 1: nx584.NX584ZoneSensor(zone1, 'motion'),
2: nx584.NX584ZoneSensor(zone2), 2: nx584.NX584ZoneSensor(zone2, 'motion'),
} }
watcher = nx584.NX584Watcher(None, zones) watcher = nx584.NX584Watcher(None, zones)
watcher._process_zone_event({'zone': 1, 'zone_state': False}) watcher._process_zone_event({'zone': 1, 'zone_state': False})