Add SmartThings Binary Sensor platform (#20699)

* Add SmartThings binary_sensor platform

* Fixed comment typo.
This commit is contained in:
Andrew Sayre 2019-02-02 16:06:30 -06:00 committed by Paulus Schoutsen
parent acf5b04231
commit 6458abca2e
3 changed files with 192 additions and 1 deletions

View file

@ -0,0 +1,81 @@
"""
Support for binary sensors through the SmartThings cloud API.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/smartthings.binary_sensor/
"""
from homeassistant.components.binary_sensor import BinarySensorDevice
from . import SmartThingsEntity
from .const import DATA_BROKERS, DOMAIN
DEPENDENCIES = ['smartthings']
CAPABILITY_TO_ATTRIB = {
'accelerationSensor': 'acceleration',
'contactSensor': 'contact',
'filterStatus': 'filterStatus',
'motionSensor': 'motion',
'presenceSensor': 'presence',
'soundSensor': 'sound',
'tamperAlert': 'tamper',
'valve': 'valve',
'waterSensor': 'water'
}
ATTRIB_TO_CLASS = {
'acceleration': 'moving',
'contact': 'opening',
'filterStatus': 'problem',
'motion': 'motion',
'presence': 'presence',
'sound': 'sound',
'tamper': 'problem',
'valve': 'opening',
'water': 'moisture'
}
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Platform uses config entry setup."""
pass
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Add binary sensors for a config entry."""
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id]
sensors = []
for device in broker.devices.values():
for capability, attrib in CAPABILITY_TO_ATTRIB.items():
if capability in device.capabilities:
sensors.append(SmartThingsBinarySensor(device, attrib))
async_add_entities(sensors)
class SmartThingsBinarySensor(SmartThingsEntity, BinarySensorDevice):
"""Define a SmartThings Binary Sensor."""
def __init__(self, device, attribute):
"""Init the class."""
super().__init__(device)
self._attribute = attribute
@property
def name(self) -> str:
"""Return the name of the binary sensor."""
return '{} {}'.format(self._device.label, self._attribute)
@property
def unique_id(self) -> str:
"""Return a unique ID."""
return '{}.{}'.format(self._device.device_id, self._attribute)
@property
def is_on(self):
"""Return true if the binary sensor is on."""
return self._device.status.is_on(self._attribute)
@property
def device_class(self):
"""Return the class of this device."""
return ATTRIB_TO_CLASS[self._attribute]

View file

@ -18,16 +18,26 @@ SETTINGS_INSTANCE_ID = "hassInstanceId"
STORAGE_KEY = DOMAIN
STORAGE_VERSION = 1
SUPPORTED_PLATFORMS = [
'binary_sensor',
'fan',
'light',
'switch'
]
SUPPORTED_CAPABILITIES = [
'accelerationSensor',
'colorControl',
'colorTemperature',
'contactSensor',
'fanSpeed',
'filterStatus',
'motionSensor',
'presenceSensor',
'soundSensor',
'switch',
'switchLevel'
'switchLevel',
'tamperAlert',
'valve',
'waterSensor'
]
VAL_UID = "^(?:([0-9a-fA-F]{32})|([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]" \
"{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}))$"

View file

@ -0,0 +1,100 @@
"""
Test for the SmartThings binary_sensor platform.
The only mocking required is of the underlying SmartThings API object so
real HTTP calls are not initiated during testing.
"""
from pysmartthings import Attribute, Capability
from homeassistant.components.smartthings import DeviceBroker, binary_sensor
from homeassistant.components.smartthings.const import (
DATA_BROKERS, DOMAIN, SIGNAL_SMARTTHINGS_UPDATE)
from homeassistant.config_entries import (
CONN_CLASS_CLOUD_PUSH, SOURCE_USER, ConfigEntry)
from homeassistant.const import ATTR_FRIENDLY_NAME
from homeassistant.helpers.dispatcher import async_dispatcher_send
async def _setup_platform(hass, *devices):
"""Set up the SmartThings binary_sensor platform and prerequisites."""
hass.config.components.add(DOMAIN)
broker = DeviceBroker(hass, devices, '')
config_entry = ConfigEntry("1", DOMAIN, "Test", {},
SOURCE_USER, CONN_CLASS_CLOUD_PUSH)
hass.data[DOMAIN] = {
DATA_BROKERS: {
config_entry.entry_id: broker
}
}
await hass.config_entries.async_forward_entry_setup(
config_entry, 'binary_sensor')
await hass.async_block_till_done()
return config_entry
async def test_async_setup_platform():
"""Test setup platform does nothing (it uses config entries)."""
await binary_sensor.async_setup_platform(None, None, None)
async def test_entity_state(hass, device_factory):
"""Tests the state attributes properly match the light types."""
device = device_factory('Motion Sensor 1', [Capability.motion_sensor],
{Attribute.motion: 'inactive'})
await _setup_platform(hass, device)
state = hass.states.get('binary_sensor.motion_sensor_1_motion')
assert state.state == 'off'
assert state.attributes[ATTR_FRIENDLY_NAME] ==\
device.label + ' ' + Attribute.motion
async def test_entity_and_device_attributes(hass, device_factory):
"""Test the attributes of the entity are correct."""
# Arrange
device = device_factory('Motion Sensor 1', [Capability.motion_sensor],
{Attribute.motion: 'inactive'})
entity_registry = await hass.helpers.entity_registry.async_get_registry()
device_registry = await hass.helpers.device_registry.async_get_registry()
# Act
await _setup_platform(hass, device)
# Assert
entity = entity_registry.async_get('binary_sensor.motion_sensor_1_motion')
assert entity
assert entity.unique_id == device.device_id + '.' + Attribute.motion
device_entry = device_registry.async_get_device(
{(DOMAIN, device.device_id)}, [])
assert device_entry
assert device_entry.name == device.label
assert device_entry.model == device.device_type_name
assert device_entry.manufacturer == 'Unavailable'
async def test_update_from_signal(hass, device_factory):
"""Test the binary_sensor updates when receiving a signal."""
# Arrange
device = device_factory('Motion Sensor 1', [Capability.motion_sensor],
{Attribute.motion: 'inactive'})
await _setup_platform(hass, device)
device.status.apply_attribute_update(
'main', Capability.motion_sensor, Attribute.motion, 'active')
# Act
async_dispatcher_send(hass, SIGNAL_SMARTTHINGS_UPDATE,
[device.device_id])
# Assert
await hass.async_block_till_done()
state = hass.states.get('binary_sensor.motion_sensor_1_motion')
assert state is not None
assert state.state == 'on'
async def test_unload_config_entry(hass, device_factory):
"""Test the binary_sensor is removed when the config entry is unloaded."""
# Arrange
device = device_factory('Motion Sensor 1', [Capability.motion_sensor],
{Attribute.motion: 'inactive'})
config_entry = await _setup_platform(hass, device)
# Act
await hass.config_entries.async_forward_entry_unload(
config_entry, 'binary_sensor')
# Assert
assert not hass.states.get('binary_sensor.motion_sensor_1_motion')