Add SmartThings button support via events (#20707)
* Add event support for buttons * binary_sensor test clean-up
This commit is contained in:
parent
3553d26f6b
commit
38ea43b678
5 changed files with 82 additions and 19 deletions
|
@ -19,7 +19,7 @@ from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
from .config_flow import SmartThingsFlowHandler # noqa
|
from .config_flow import SmartThingsFlowHandler # noqa
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_APP_ID, CONF_INSTALLED_APP_ID, DATA_BROKERS, DATA_MANAGER, DOMAIN,
|
CONF_APP_ID, CONF_INSTALLED_APP_ID, DATA_BROKERS, DATA_MANAGER, DOMAIN,
|
||||||
SIGNAL_SMARTTHINGS_UPDATE, SUPPORTED_PLATFORMS)
|
EVENT_BUTTON, SIGNAL_SMARTTHINGS_UPDATE, SUPPORTED_PLATFORMS)
|
||||||
from .smartapp import (
|
from .smartapp import (
|
||||||
setup_smartapp, setup_smartapp_endpoint, validate_installed_app)
|
setup_smartapp, setup_smartapp_endpoint, validate_installed_app)
|
||||||
|
|
||||||
|
@ -154,6 +154,19 @@ class DeviceBroker:
|
||||||
continue
|
continue
|
||||||
device.status.apply_attribute_update(
|
device.status.apply_attribute_update(
|
||||||
evt.component_id, evt.capability, evt.attribute, evt.value)
|
evt.component_id, evt.capability, evt.attribute, evt.value)
|
||||||
|
|
||||||
|
# Fire events for buttons
|
||||||
|
if evt.capability == 'button' and evt.attribute == 'button':
|
||||||
|
data = {
|
||||||
|
'component_id': evt.component_id,
|
||||||
|
'device_id': evt.device_id,
|
||||||
|
'location_id': evt.location_id,
|
||||||
|
'value': evt.value,
|
||||||
|
'name': device.label
|
||||||
|
}
|
||||||
|
self._hass.bus.async_fire(EVENT_BUTTON, data)
|
||||||
|
_LOGGER.debug("Fired button event: %s", data)
|
||||||
|
|
||||||
updated_devices.add(device.device_id)
|
updated_devices.add(device.device_id)
|
||||||
_LOGGER.debug("Update received with %s events and updated %s devices",
|
_LOGGER.debug("Update received with %s events and updated %s devices",
|
||||||
len(req.events), len(updated_devices))
|
len(req.events), len(updated_devices))
|
||||||
|
|
|
@ -12,6 +12,7 @@ CONF_LOCATION_ID = 'location_id'
|
||||||
DATA_MANAGER = 'manager'
|
DATA_MANAGER = 'manager'
|
||||||
DATA_BROKERS = 'brokers'
|
DATA_BROKERS = 'brokers'
|
||||||
DOMAIN = 'smartthings'
|
DOMAIN = 'smartthings'
|
||||||
|
EVENT_BUTTON = "smartthings.button"
|
||||||
SIGNAL_SMARTTHINGS_UPDATE = 'smartthings_update'
|
SIGNAL_SMARTTHINGS_UPDATE = 'smartthings_update'
|
||||||
SIGNAL_SMARTAPP_PREFIX = 'smartthings_smartap_'
|
SIGNAL_SMARTAPP_PREFIX = 'smartthings_smartap_'
|
||||||
SETTINGS_INSTANCE_ID = "hassInstanceId"
|
SETTINGS_INSTANCE_ID = "hassInstanceId"
|
||||||
|
@ -25,6 +26,7 @@ SUPPORTED_PLATFORMS = [
|
||||||
]
|
]
|
||||||
SUPPORTED_CAPABILITIES = [
|
SUPPORTED_CAPABILITIES = [
|
||||||
'accelerationSensor',
|
'accelerationSensor',
|
||||||
|
'button',
|
||||||
'colorControl',
|
'colorControl',
|
||||||
'colorTemperature',
|
'colorTemperature',
|
||||||
'contactSensor',
|
'contactSensor',
|
||||||
|
|
|
@ -254,14 +254,16 @@ def device_factory_fixture():
|
||||||
@pytest.fixture(name="event_factory")
|
@pytest.fixture(name="event_factory")
|
||||||
def event_factory_fixture():
|
def event_factory_fixture():
|
||||||
"""Fixture for creating mock devices."""
|
"""Fixture for creating mock devices."""
|
||||||
def _factory(device_id, event_type="DEVICE_EVENT"):
|
def _factory(device_id, event_type="DEVICE_EVENT", capability='',
|
||||||
|
attribute='Updated', value='Value'):
|
||||||
event = Mock()
|
event = Mock()
|
||||||
event.event_type = event_type
|
event.event_type = event_type
|
||||||
event.device_id = device_id
|
event.device_id = device_id
|
||||||
event.component_id = 'main'
|
event.component_id = 'main'
|
||||||
event.capability = ''
|
event.capability = capability
|
||||||
event.attribute = 'Updated'
|
event.attribute = attribute
|
||||||
event.value = 'Value'
|
event.value = value
|
||||||
|
event.location_id = str(uuid4())
|
||||||
return event
|
return event
|
||||||
return _factory
|
return _factory
|
||||||
|
|
||||||
|
@ -269,11 +271,15 @@ def event_factory_fixture():
|
||||||
@pytest.fixture(name="event_request_factory")
|
@pytest.fixture(name="event_request_factory")
|
||||||
def event_request_factory_fixture(event_factory):
|
def event_request_factory_fixture(event_factory):
|
||||||
"""Fixture for creating mock smartapp event requests."""
|
"""Fixture for creating mock smartapp event requests."""
|
||||||
def _factory(device_ids):
|
def _factory(device_ids=None, events=None):
|
||||||
request = Mock()
|
request = Mock()
|
||||||
request.installed_app_id = uuid4()
|
request.installed_app_id = uuid4()
|
||||||
request.events = [event_factory(id) for id in device_ids]
|
if events is None:
|
||||||
request.events.append(event_factory(uuid4()))
|
events = []
|
||||||
request.events.append(event_factory(device_ids[0], event_type="OTHER"))
|
if device_ids:
|
||||||
|
events.extend([event_factory(id) for id in device_ids])
|
||||||
|
events.append(event_factory(uuid4()))
|
||||||
|
events.append(event_factory(device_ids[0], event_type="OTHER"))
|
||||||
|
request.events = events
|
||||||
return request
|
return request
|
||||||
return _factory
|
return _factory
|
||||||
|
|
|
@ -6,9 +6,10 @@ real HTTP calls are not initiated during testing.
|
||||||
"""
|
"""
|
||||||
from pysmartthings import Attribute, Capability
|
from pysmartthings import Attribute, Capability
|
||||||
|
|
||||||
|
from homeassistant.components.binary_sensor import DEVICE_CLASSES
|
||||||
from homeassistant.components.smartthings import DeviceBroker, binary_sensor
|
from homeassistant.components.smartthings import DeviceBroker, binary_sensor
|
||||||
from homeassistant.components.smartthings.const import (
|
from homeassistant.components.smartthings.const import (
|
||||||
DATA_BROKERS, DOMAIN, SIGNAL_SMARTTHINGS_UPDATE)
|
DATA_BROKERS, DOMAIN, SIGNAL_SMARTTHINGS_UPDATE, SUPPORTED_CAPABILITIES)
|
||||||
from homeassistant.config_entries import (
|
from homeassistant.config_entries import (
|
||||||
CONN_CLASS_CLOUD_PUSH, SOURCE_USER, ConfigEntry)
|
CONN_CLASS_CLOUD_PUSH, SOURCE_USER, ConfigEntry)
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME
|
from homeassistant.const import ATTR_FRIENDLY_NAME
|
||||||
|
@ -32,6 +33,18 @@ async def _setup_platform(hass, *devices):
|
||||||
return config_entry
|
return config_entry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_mapping_integrity():
|
||||||
|
"""Test ensures the map dicts have proper integrity."""
|
||||||
|
# Ensure every CAPABILITY_TO_ATTRIB key is in SUPPORTED_CAPABILITIES
|
||||||
|
# Ensure every CAPABILITY_TO_ATTRIB value is in ATTRIB_TO_CLASS keys
|
||||||
|
for capability, attrib in binary_sensor.CAPABILITY_TO_ATTRIB.items():
|
||||||
|
assert capability in SUPPORTED_CAPABILITIES, capability
|
||||||
|
assert attrib in binary_sensor.ATTRIB_TO_CLASS.keys(), attrib
|
||||||
|
# Ensure every ATTRIB_TO_CLASS value is in DEVICE_CLASSES
|
||||||
|
for device_class in binary_sensor.ATTRIB_TO_CLASS.values():
|
||||||
|
assert device_class in DEVICE_CLASSES
|
||||||
|
|
||||||
|
|
||||||
async def test_async_setup_platform():
|
async def test_async_setup_platform():
|
||||||
"""Test setup platform does nothing (it uses config entries)."""
|
"""Test setup platform does nothing (it uses config entries)."""
|
||||||
await binary_sensor.async_setup_platform(None, None, None)
|
await binary_sensor.async_setup_platform(None, None, None)
|
||||||
|
@ -58,15 +71,15 @@ async def test_entity_and_device_attributes(hass, device_factory):
|
||||||
# Act
|
# Act
|
||||||
await _setup_platform(hass, device)
|
await _setup_platform(hass, device)
|
||||||
# Assert
|
# Assert
|
||||||
entity = entity_registry.async_get('binary_sensor.motion_sensor_1_motion')
|
entry = entity_registry.async_get('binary_sensor.motion_sensor_1_motion')
|
||||||
assert entity
|
assert entry
|
||||||
assert entity.unique_id == device.device_id + '.' + Attribute.motion
|
assert entry.unique_id == device.device_id + '.' + Attribute.motion
|
||||||
device_entry = device_registry.async_get_device(
|
entry = device_registry.async_get_device(
|
||||||
{(DOMAIN, device.device_id)}, [])
|
{(DOMAIN, device.device_id)}, [])
|
||||||
assert device_entry
|
assert entry
|
||||||
assert device_entry.name == device.label
|
assert entry.name == device.label
|
||||||
assert device_entry.model == device.device_type_name
|
assert entry.model == device.device_type_name
|
||||||
assert device_entry.manufacturer == 'Unavailable'
|
assert entry.manufacturer == 'Unavailable'
|
||||||
|
|
||||||
|
|
||||||
async def test_update_from_signal(hass, device_factory):
|
async def test_update_from_signal(hass, device_factory):
|
||||||
|
|
|
@ -8,7 +8,8 @@ import pytest
|
||||||
|
|
||||||
from homeassistant.components import smartthings
|
from homeassistant.components import smartthings
|
||||||
from homeassistant.components.smartthings.const import (
|
from homeassistant.components.smartthings.const import (
|
||||||
DATA_BROKERS, DOMAIN, SIGNAL_SMARTTHINGS_UPDATE, SUPPORTED_PLATFORMS)
|
DATA_BROKERS, DOMAIN, EVENT_BUTTON, SIGNAL_SMARTTHINGS_UPDATE,
|
||||||
|
SUPPORTED_PLATFORMS)
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
|
@ -181,3 +182,31 @@ async def test_event_handler_ignores_other_installed_app(
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert not called
|
assert not called
|
||||||
|
|
||||||
|
|
||||||
|
async def test_event_handler_fires_button_events(
|
||||||
|
hass, device_factory, event_factory, event_request_factory):
|
||||||
|
"""Test the event handler fires button events."""
|
||||||
|
device = device_factory('Button 1', ['button'])
|
||||||
|
event = event_factory(device.device_id, capability='button',
|
||||||
|
attribute='button', value='pushed')
|
||||||
|
request = event_request_factory(events=[event])
|
||||||
|
called = False
|
||||||
|
|
||||||
|
def handler(evt):
|
||||||
|
nonlocal called
|
||||||
|
called = True
|
||||||
|
assert evt.data == {
|
||||||
|
'component_id': 'main',
|
||||||
|
'device_id': device.device_id,
|
||||||
|
'location_id': event.location_id,
|
||||||
|
'value': 'pushed',
|
||||||
|
'name': device.label
|
||||||
|
}
|
||||||
|
hass.bus.async_listen(EVENT_BUTTON, handler)
|
||||||
|
broker = smartthings.DeviceBroker(
|
||||||
|
hass, [device], request.installed_app_id)
|
||||||
|
await broker.event_handler(request, None, None)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert called
|
||||||
|
|
Loading…
Add table
Reference in a new issue