Add SmartThings Binary Sensor platform (#20699)
* Add SmartThings binary_sensor platform * Fixed comment typo.
This commit is contained in:
parent
acf5b04231
commit
6458abca2e
3 changed files with 192 additions and 1 deletions
81
homeassistant/components/smartthings/binary_sensor.py
Normal file
81
homeassistant/components/smartthings/binary_sensor.py
Normal 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]
|
|
@ -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}))$"
|
||||
|
|
100
tests/components/smartthings/test_binary_sensor.py
Normal file
100
tests/components/smartthings/test_binary_sensor.py
Normal 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')
|
Loading…
Add table
Reference in a new issue