mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 23:27:37 +00:00
Report states (#11973)
* Refactor Alexa Smart Home API Having an object per interface will make it easier to support properties. Ideally, properties are reported in context in all responses. However current implementation reports them only in response to a ReportState request. This seems to work sufficiently. As long as the device is opened in the Alexa app, Amazon will poll the device state every few seconds with a ReportState request. * Report properties for some Alexa interfaces Fixes (mostly) #11874. Other interfaces will need properties implemented as well. Implementing properties for just PowerController seems sufficient to eliminate the "There was a problem." error for any device that supports it, even if other interfaces are supported. Of course the additional properties will be reported incorrectly in the Alexa app. Includes a minor bugfix: `reportable` was previously placed incorrectly in the responses, so Amazon was ignoring it.
This commit is contained in:
parent
84711aad90
commit
7d6ef4445e
@ -17,7 +17,7 @@ from homeassistant.const import (
|
||||
SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_MEDIA_STOP,
|
||||
SERVICE_SET_COVER_POSITION, SERVICE_TURN_OFF, SERVICE_TURN_ON,
|
||||
SERVICE_UNLOCK, SERVICE_VOLUME_SET, TEMP_FAHRENHEIT, TEMP_CELSIUS,
|
||||
CONF_UNIT_OF_MEASUREMENT)
|
||||
CONF_UNIT_OF_MEASUREMENT, STATE_LOCKED, STATE_UNLOCKED, STATE_ON)
|
||||
from .const import CONF_FILTER, CONF_ENTITY_CONFIG
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -245,9 +245,8 @@ class _AlexaInterface(object):
|
||||
'properties': {
|
||||
'supported': self.properties_supported(),
|
||||
'proactivelyReported': self.properties_proactively_reported(),
|
||||
'retrievable': self.properties_retrievable(),
|
||||
},
|
||||
# XXX this is incorrect, but the tests assert it
|
||||
'retrievable': self.properties_retrievable(),
|
||||
}
|
||||
|
||||
# pylint: disable=assignment-from-none
|
||||
@ -271,11 +270,41 @@ class _AlexaPowerController(_AlexaInterface):
|
||||
def name(self):
|
||||
return 'Alexa.PowerController'
|
||||
|
||||
def properties_supported(self):
|
||||
return [{'name': 'powerState'}]
|
||||
|
||||
def properties_retrievable(self):
|
||||
return True
|
||||
|
||||
def get_property(self, name):
|
||||
if name != 'powerState':
|
||||
raise _UnsupportedProperty(name)
|
||||
|
||||
if self.entity.state == STATE_ON:
|
||||
return 'ON'
|
||||
return 'OFF'
|
||||
|
||||
|
||||
class _AlexaLockController(_AlexaInterface):
|
||||
def name(self):
|
||||
return 'Alexa.LockController'
|
||||
|
||||
def properties_supported(self):
|
||||
return [{'name': 'lockState'}]
|
||||
|
||||
def properties_retrievable(self):
|
||||
return True
|
||||
|
||||
def get_property(self, name):
|
||||
if name != 'lockState':
|
||||
raise _UnsupportedProperty(name)
|
||||
|
||||
if self.entity.state == STATE_LOCKED:
|
||||
return 'LOCKED'
|
||||
elif self.entity.state == STATE_UNLOCKED:
|
||||
return 'UNLOCKED'
|
||||
return 'JAMMED'
|
||||
|
||||
|
||||
class _AlexaSceneController(_AlexaInterface):
|
||||
def __init__(self, entity, supports_deactivation):
|
||||
@ -290,6 +319,18 @@ class _AlexaBrightnessController(_AlexaInterface):
|
||||
def name(self):
|
||||
return 'Alexa.BrightnessController'
|
||||
|
||||
def properties_supported(self):
|
||||
return [{'name': 'brightness'}]
|
||||
|
||||
def properties_retrievable(self):
|
||||
return True
|
||||
|
||||
def get_property(self, name):
|
||||
if name != 'brightness':
|
||||
raise _UnsupportedProperty(name)
|
||||
|
||||
return round(self.entity.attributes['brightness'] / 255.0 * 100)
|
||||
|
||||
|
||||
class _AlexaColorController(_AlexaInterface):
|
||||
def name(self):
|
||||
|
@ -5,9 +5,11 @@ from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.const import TEMP_FAHRENHEIT, CONF_UNIT_OF_MEASUREMENT
|
||||
from homeassistant.const import (
|
||||
TEMP_FAHRENHEIT, CONF_UNIT_OF_MEASUREMENT, STATE_LOCKED, STATE_UNLOCKED,
|
||||
STATE_UNKNOWN, STATE_ON, STATE_OFF)
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components import alexa
|
||||
from homeassistant.components import alexa, light
|
||||
from homeassistant.components.alexa import smart_home
|
||||
from homeassistant.helpers import entityfilter
|
||||
|
||||
@ -379,8 +381,8 @@ def test_discovery_request(hass):
|
||||
assert len(appliance['capabilities']) == 1
|
||||
capability = appliance['capabilities'][0]
|
||||
assert capability['interface'] == 'Alexa.TemperatureSensor'
|
||||
assert capability['retrievable'] is True
|
||||
properties = capability['properties']
|
||||
assert properties['retrievable'] is True
|
||||
assert {'name': 'temperature'} in properties['supported']
|
||||
continue
|
||||
|
||||
@ -1248,6 +1250,99 @@ def test_api_report_temperature(hass):
|
||||
assert prop['value'] == {'value': 42.0, 'scale': 'FAHRENHEIT'}
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_report_lock_state(hass):
|
||||
"""Test LockController implements lockState property."""
|
||||
hass.states.async_set(
|
||||
'lock.locked', STATE_LOCKED, {})
|
||||
hass.states.async_set(
|
||||
'lock.unlocked', STATE_UNLOCKED, {})
|
||||
hass.states.async_set(
|
||||
'lock.unknown', STATE_UNKNOWN, {})
|
||||
|
||||
request = get_new_request('Alexa', 'ReportState', 'lock#locked')
|
||||
msg = yield from smart_home.async_handle_message(
|
||||
hass, DEFAULT_CONFIG, request)
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
properties = msg['context']['properties']
|
||||
assert len(properties) == 1
|
||||
prop = properties[0]
|
||||
assert prop['namespace'] == 'Alexa.LockController'
|
||||
assert prop['name'] == 'lockState'
|
||||
assert prop['value'] == 'LOCKED'
|
||||
|
||||
request = get_new_request('Alexa', 'ReportState', 'lock#unlocked')
|
||||
msg = yield from smart_home.async_handle_message(
|
||||
hass, DEFAULT_CONFIG, request)
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
properties = msg['context']['properties']
|
||||
prop = properties[0]
|
||||
assert prop['value'] == 'UNLOCKED'
|
||||
|
||||
request = get_new_request('Alexa', 'ReportState', 'lock#unknown')
|
||||
msg = yield from smart_home.async_handle_message(
|
||||
hass, DEFAULT_CONFIG, request)
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
properties = msg['context']['properties']
|
||||
prop = properties[0]
|
||||
assert prop['value'] == 'JAMMED'
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_report_power_state(hass):
|
||||
"""Test PowerController implements powerState property."""
|
||||
hass.states.async_set(
|
||||
'switch.on', STATE_ON, {})
|
||||
hass.states.async_set(
|
||||
'switch.off', STATE_OFF, {})
|
||||
|
||||
request = get_new_request('Alexa', 'ReportState', 'switch#on')
|
||||
msg = yield from smart_home.async_handle_message(
|
||||
hass, DEFAULT_CONFIG, request)
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
properties = msg['context']['properties']
|
||||
assert len(properties) == 1
|
||||
prop = properties[0]
|
||||
assert prop['namespace'] == 'Alexa.PowerController'
|
||||
assert prop['name'] == 'powerState'
|
||||
assert prop['value'] == 'ON'
|
||||
|
||||
request = get_new_request('Alexa', 'ReportState', 'switch#off')
|
||||
msg = yield from smart_home.async_handle_message(
|
||||
hass, DEFAULT_CONFIG, request)
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_report_brightness(hass):
|
||||
"""Test BrightnessController implements brightness property."""
|
||||
hass.states.async_set(
|
||||
'light.test', STATE_ON, {
|
||||
'brightness': 128,
|
||||
'supported_features': light.SUPPORT_BRIGHTNESS,
|
||||
}
|
||||
)
|
||||
|
||||
request = get_new_request('Alexa', 'ReportState', 'light.test')
|
||||
msg = yield from smart_home.async_handle_message(
|
||||
hass, DEFAULT_CONFIG, request)
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
for prop in msg['context']['properties']:
|
||||
if (
|
||||
prop['namespace'] == 'Alexa.BrightnessController'
|
||||
and prop['name'] == 'brightness'
|
||||
):
|
||||
assert prop['value'] == 50
|
||||
break
|
||||
else:
|
||||
assert False, 'no brightness property present'
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_entity_config(hass):
|
||||
"""Test that we can configure things via entity config."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user