diff --git a/homeassistant/components/alexa/smart_home.py b/homeassistant/components/alexa/smart_home.py index f88d81ab851..df477a3b653 100644 --- a/homeassistant/components/alexa/smart_home.py +++ b/homeassistant/components/alexa/smart_home.py @@ -5,15 +5,16 @@ from datetime import datetime from uuid import uuid4 from homeassistant.components import ( - alert, automation, cover, climate, fan, group, input_boolean, light, lock, - media_player, scene, script, switch, http, sensor) + alert, automation, binary_sensor, cover, climate, fan, group, + input_boolean, light, lock, media_player, scene, script, switch, http, + sensor) import homeassistant.core as ha import homeassistant.util.color as color_util from homeassistant.util.temperature import convert as convert_temperature from homeassistant.util.decorator import Registry from homeassistant.const import ( - ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, - ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, SERVICE_LOCK, + ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, + ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, SERVICE_LOCK, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP, SERVICE_SET_COVER_POSITION, SERVICE_TURN_OFF, SERVICE_TURN_ON, @@ -71,6 +72,9 @@ class _DisplayCategory: # Indicates media devices with video or photo capabilities. CAMERA = "CAMERA" + # Indicates an endpoint that detects and reports contact. + CONTACT_SENSOR = "CONTACT_SENSOR" + # Indicates a door. DOOR = "DOOR" @@ -414,6 +418,29 @@ class _AlexaTemperatureSensor(_AlexaInterface): } +class _AlexaContactSensor(_AlexaInterface): + def __init__(self, hass, entity): + _AlexaInterface.__init__(self, entity) + self.hass = hass + + def name(self): + return 'Alexa.ContactSensor' + + def properties_supported(self): + return [{'name': 'detectionState'}] + + def properties_retrievable(self): + return True + + def get_property(self, name): + if name != 'detectionState': + raise _UnsupportedProperty(name) + + if self.entity.state == STATE_ON: + return 'DETECTED' + return 'NOT_DETECTED' + + class _AlexaThermostatController(_AlexaInterface): def __init__(self, hass, entity): _AlexaInterface.__init__(self, entity) @@ -625,6 +652,32 @@ class _SensorCapabilities(_AlexaEntity): yield _AlexaTemperatureSensor(self.hass, self.entity) +@ENTITY_ADAPTERS.register(binary_sensor.DOMAIN) +class _BinarySensorCapabilities(_AlexaEntity): + TYPE_CONTACT = 'contact' + + def default_display_categories(self): + sensor_type = self.get_type() + if sensor_type is self.TYPE_CONTACT: + return [_DisplayCategory.CONTACT_SENSOR] + + def interfaces(self): + sensor_type = self.get_type() + if sensor_type is self.TYPE_CONTACT: + yield _AlexaContactSensor(self.hass, self.entity) + + def get_type(self): + """Return the type of binary sensor.""" + attrs = self.entity.attributes + if attrs.get(ATTR_DEVICE_CLASS) in ( + 'door', + 'garage_door', + 'opening', + 'window', + ): + return self.TYPE_CONTACT + + class _Cause: """Possible causes for property changes. diff --git a/tests/components/alexa/test_smart_home.py b/tests/components/alexa/test_smart_home.py index 88c69194407..9596f341447 100644 --- a/tests/components/alexa/test_smart_home.py +++ b/tests/components/alexa/test_smart_home.py @@ -694,6 +694,37 @@ def test_temp_sensor(hass): {'value': 42.0, 'scale': 'FAHRENHEIT'}) +@asyncio.coroutine +def test_contact_sensor(hass): + """Test contact sensor discovery.""" + device = ( + 'binary_sensor.test_contact', + 'on', + { + 'friendly_name': "Test Contact Sensor", + 'device_class': 'door', + } + ) + appliance = yield from discovery_test(device, hass) + + assert appliance['endpointId'] == 'binary_sensor#test_contact' + assert appliance['displayCategories'][0] == 'CONTACT_SENSOR' + assert appliance['friendlyName'] == 'Test Contact Sensor' + + (capability,) = assert_endpoint_capabilities( + appliance, + 'Alexa.ContactSensor') + assert capability['interface'] == 'Alexa.ContactSensor' + properties = capability['properties'] + assert properties['retrievable'] is True + assert {'name': 'detectionState'} in properties['supported'] + + properties = yield from reported_properties(hass, + 'binary_sensor#test_contact') + properties.assert_equal('Alexa.ContactSensor', 'detectionState', + 'DETECTED') + + @asyncio.coroutine def test_unknown_sensor(hass): """Test sensors of unknown quantities are not discovered."""