Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions homeassistant/components/smartthings/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -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]
Comment thread
andrewsayre marked this conversation as resolved.
12 changes: 11 additions & 1 deletion homeassistant/components/smartthings/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,26 @@
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}))$"
Expand Down
100 changes: 100 additions & 0 deletions tests/components/smartthings/test_binary_sensor.py
Original file line number Diff line number Diff line change
@@ -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')
Comment thread
andrewsayre marked this conversation as resolved.
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')