2017-09-16 19:35:28 +00:00
|
|
|
"""Test for smart home alexa support."""
|
|
|
|
import pytest
|
|
|
|
|
2019-12-08 13:56:42 +00:00
|
|
|
from homeassistant.components.alexa import messages, smart_home
|
2019-10-23 15:28:23 +00:00
|
|
|
from homeassistant.components.media_player.const import (
|
|
|
|
SUPPORT_NEXT_TRACK,
|
|
|
|
SUPPORT_PAUSE,
|
|
|
|
SUPPORT_PLAY,
|
|
|
|
SUPPORT_PLAY_MEDIA,
|
|
|
|
SUPPORT_PREVIOUS_TRACK,
|
2019-10-31 09:38:44 +00:00
|
|
|
SUPPORT_SEEK,
|
2019-12-24 22:06:39 +00:00
|
|
|
SUPPORT_SELECT_SOUND_MODE,
|
2019-10-23 15:28:23 +00:00
|
|
|
SUPPORT_SELECT_SOURCE,
|
|
|
|
SUPPORT_STOP,
|
|
|
|
SUPPORT_TURN_OFF,
|
|
|
|
SUPPORT_TURN_ON,
|
|
|
|
SUPPORT_VOLUME_MUTE,
|
|
|
|
SUPPORT_VOLUME_SET,
|
|
|
|
)
|
2019-12-08 13:56:42 +00:00
|
|
|
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
|
|
|
|
from homeassistant.core import Context, callback
|
2017-11-18 05:10:24 +00:00
|
|
|
from homeassistant.helpers import entityfilter
|
2017-09-16 19:35:28 +00:00
|
|
|
|
2019-06-13 15:43:57 +00:00
|
|
|
from . import (
|
|
|
|
DEFAULT_CONFIG,
|
2019-12-08 13:56:42 +00:00
|
|
|
MockConfig,
|
2019-06-13 15:43:57 +00:00
|
|
|
ReportedProperties,
|
|
|
|
assert_power_controller_works,
|
2019-12-08 13:56:42 +00:00
|
|
|
assert_request_calls_service,
|
|
|
|
assert_request_fails,
|
2019-06-13 15:43:57 +00:00
|
|
|
assert_scene_controller_works,
|
2019-12-08 13:56:42 +00:00
|
|
|
get_new_request,
|
2019-06-13 15:43:57 +00:00
|
|
|
reported_properties,
|
|
|
|
)
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-12-08 13:56:42 +00:00
|
|
|
from tests.common import async_mock_service
|
|
|
|
|
2017-09-16 19:35:28 +00:00
|
|
|
|
2018-08-20 12:18:07 +00:00
|
|
|
@pytest.fixture
|
|
|
|
def events(hass):
|
|
|
|
"""Fixture that catches alexa events."""
|
|
|
|
events = []
|
|
|
|
hass.bus.async_listen(
|
2019-07-31 19:25:30 +00:00
|
|
|
smart_home.EVENT_ALEXA_SMART_HOME, callback(lambda e: events.append(e))
|
2018-08-20 12:18:07 +00:00
|
|
|
)
|
|
|
|
yield events
|
|
|
|
|
|
|
|
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
def test_create_api_message_defaults(hass):
|
2017-10-07 20:31:57 +00:00
|
|
|
"""Create a API message response of a request with defaults."""
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.PowerController", "TurnOn", "switch#xy")
|
|
|
|
directive_header = request["directive"]["header"]
|
2019-06-13 15:43:57 +00:00
|
|
|
directive = messages.AlexaDirective(request)
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
msg = directive.response(payload={"test": 3})._response
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
2017-09-16 19:35:28 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["header"]["messageId"] is not None
|
|
|
|
assert msg["header"]["messageId"] != directive_header["messageId"]
|
|
|
|
assert msg["header"]["correlationToken"] == directive_header["correlationToken"]
|
|
|
|
assert msg["header"]["name"] == "Response"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa"
|
|
|
|
assert msg["header"]["payloadVersion"] == "3"
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "test" in msg["payload"]
|
|
|
|
assert msg["payload"]["test"] == 3
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["endpoint"] == request["directive"]["endpoint"]
|
|
|
|
assert msg["endpoint"] is not request["directive"]["endpoint"]
|
2017-10-07 20:31:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_create_api_message_special():
|
|
|
|
"""Create a API message response of a request with non defaults."""
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.PowerController", "TurnOn")
|
|
|
|
directive_header = request["directive"]["header"]
|
|
|
|
directive_header.pop("correlationToken")
|
2019-06-13 15:43:57 +00:00
|
|
|
directive = messages.AlexaDirective(request)
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
msg = directive.response("testName", "testNameSpace")._response
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["header"]["messageId"] is not None
|
|
|
|
assert msg["header"]["messageId"] != directive_header["messageId"]
|
|
|
|
assert "correlationToken" not in msg["header"]
|
|
|
|
assert msg["header"]["name"] == "testName"
|
|
|
|
assert msg["header"]["namespace"] == "testNameSpace"
|
|
|
|
assert msg["header"]["payloadVersion"] == "3"
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["payload"] == {}
|
|
|
|
assert "endpoint" not in msg
|
2017-09-16 19:35:28 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_wrong_version(hass):
|
2017-09-16 19:35:28 +00:00
|
|
|
"""Test with wrong version."""
|
2019-07-31 19:25:30 +00:00
|
|
|
msg = get_new_request("Alexa.PowerController", "TurnOn")
|
|
|
|
msg["directive"]["header"]["payloadVersion"] = "2"
|
2017-09-16 19:35:28 +00:00
|
|
|
|
|
|
|
with pytest.raises(AssertionError):
|
2018-10-29 21:57:27 +00:00
|
|
|
await smart_home.async_handle_message(hass, DEFAULT_CONFIG, msg)
|
2017-09-16 19:35:28 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def discovery_test(device, hass, expected_endpoints=1):
|
2017-09-16 19:35:28 +00:00
|
|
|
"""Test alexa discovery request."""
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.Discovery", "Discover")
|
2017-09-16 19:35:28 +00:00
|
|
|
|
2017-11-17 17:14:22 +00:00
|
|
|
# setup test devices
|
2018-01-30 04:33:39 +00:00
|
|
|
hass.states.async_set(*device)
|
2017-09-16 19:35:28 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request)
|
2017-09-16 19:35:28 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
2017-11-16 05:44:27 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["header"]["name"] == "Discover.Response"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa.Discovery"
|
|
|
|
endpoints = msg["payload"]["endpoints"]
|
2018-01-30 04:33:39 +00:00
|
|
|
assert len(endpoints) == expected_endpoints
|
2017-11-17 17:14:22 +00:00
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
if expected_endpoints == 1:
|
|
|
|
return endpoints[0]
|
2018-07-23 08:16:05 +00:00
|
|
|
if expected_endpoints > 1:
|
2018-01-30 04:33:39 +00:00
|
|
|
return endpoints
|
|
|
|
return None
|
2017-11-17 17:14:22 +00:00
|
|
|
|
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
def get_capability(capabilities, capability_name, instance=None):
|
2019-01-10 23:52:21 +00:00
|
|
|
"""Search a set of capabilities for a specific one."""
|
|
|
|
for capability in capabilities:
|
2019-12-19 11:44:17 +00:00
|
|
|
if instance and capability["instance"] == instance:
|
|
|
|
return capability
|
|
|
|
elif capability["interface"] == capability_name:
|
2019-01-10 23:52:21 +00:00
|
|
|
return capability
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
def assert_endpoint_capabilities(endpoint, *interfaces):
|
|
|
|
"""Assert the endpoint supports the given interfaces.
|
2017-11-17 17:14:22 +00:00
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
Returns a set of capabilities, in case you want to assert more things about
|
|
|
|
them.
|
|
|
|
"""
|
2019-07-31 19:25:30 +00:00
|
|
|
capabilities = endpoint["capabilities"]
|
|
|
|
supported = set(feature["interface"] for feature in capabilities)
|
2017-11-17 17:14:22 +00:00
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
assert supported == set(interfaces)
|
|
|
|
return capabilities
|
2017-11-17 17:14:22 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_switch(hass, events):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test switch discovery."""
|
2019-07-31 19:25:30 +00:00
|
|
|
device = ("switch.test", "on", {"friendly_name": "Test switch"})
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2017-11-17 17:14:22 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "switch#test"
|
|
|
|
assert appliance["displayCategories"][0] == "SWITCH"
|
|
|
|
assert appliance["friendlyName"] == "Test switch"
|
2019-01-10 23:52:21 +00:00
|
|
|
assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
2017-11-17 17:14:22 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_power_controller_works(
|
2019-07-31 19:25:30 +00:00
|
|
|
"switch#test", "switch.turn_on", "switch.turn_off", hass
|
|
|
|
)
|
2017-11-17 17:14:22 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = await reported_properties(hass, "switch#test")
|
|
|
|
properties.assert_equal("Alexa.PowerController", "powerState", "ON")
|
2018-01-26 18:40:39 +00:00
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-11-25 22:50:43 +00:00
|
|
|
async def test_outlet(hass, events):
|
|
|
|
"""Test switch with device class outlet discovery."""
|
|
|
|
device = (
|
|
|
|
"switch.test",
|
|
|
|
"on",
|
|
|
|
{"friendly_name": "Test switch", "device_class": "outlet"},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "switch#test"
|
|
|
|
assert appliance["displayCategories"][0] == "SMARTPLUG"
|
|
|
|
assert appliance["friendlyName"] == "Test switch"
|
|
|
|
assert_endpoint_capabilities(
|
2019-11-26 15:17:41 +00:00
|
|
|
appliance, "Alexa", "Alexa.PowerController", "Alexa.EndpointHealth"
|
2019-11-25 22:50:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_light(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test light discovery."""
|
2019-07-31 19:25:30 +00:00
|
|
|
device = ("light.test_1", "on", {"friendly_name": "Test light 1"})
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "light#test_1"
|
|
|
|
assert appliance["displayCategories"][0] == "LIGHT"
|
|
|
|
assert appliance["friendlyName"] == "Test light 1"
|
2019-01-10 23:52:21 +00:00
|
|
|
assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_power_controller_works(
|
2019-07-31 19:25:30 +00:00
|
|
|
"light#test_1", "light.turn_on", "light.turn_off", hass
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_dimmable_light(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test dimmable light discovery."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"light.test_2",
|
|
|
|
"on",
|
|
|
|
{"brightness": 128, "friendly_name": "Test light 2", "supported_features": 1},
|
|
|
|
)
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-26 18:40:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "light#test_2"
|
|
|
|
assert appliance["displayCategories"][0] == "LIGHT"
|
|
|
|
assert appliance["friendlyName"] == "Test light 2"
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
assert_endpoint_capabilities(
|
|
|
|
appliance,
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.BrightnessController",
|
|
|
|
"Alexa.PowerController",
|
|
|
|
"Alexa.EndpointHealth",
|
2019-11-26 07:05:10 +00:00
|
|
|
"Alexa",
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
2017-09-16 19:35:28 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = await reported_properties(hass, "light#test_2")
|
|
|
|
properties.assert_equal("Alexa.PowerController", "powerState", "ON")
|
|
|
|
properties.assert_equal("Alexa.BrightnessController", "brightness", 50)
|
2017-09-16 19:35:28 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.BrightnessController",
|
|
|
|
"SetBrightness",
|
|
|
|
"light#test_2",
|
|
|
|
"light.turn_on",
|
2018-01-30 04:33:39 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"brightness": "50"},
|
|
|
|
)
|
|
|
|
assert call.data["brightness_pct"] == 50
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_color_light(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test color light discovery."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"light.test_3",
|
|
|
|
"on",
|
2018-01-30 04:33:39 +00:00
|
|
|
{
|
2019-07-31 19:25:30 +00:00
|
|
|
"friendly_name": "Test light 3",
|
|
|
|
"supported_features": 19,
|
|
|
|
"min_mireds": 142,
|
|
|
|
"color_temp": "333",
|
|
|
|
},
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "light#test_3"
|
|
|
|
assert appliance["displayCategories"][0] == "LIGHT"
|
|
|
|
assert appliance["friendlyName"] == "Test light 3"
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
assert_endpoint_capabilities(
|
|
|
|
appliance,
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.BrightnessController",
|
|
|
|
"Alexa.PowerController",
|
|
|
|
"Alexa.ColorController",
|
|
|
|
"Alexa.ColorTemperatureController",
|
|
|
|
"Alexa.EndpointHealth",
|
2019-11-26 07:05:10 +00:00
|
|
|
"Alexa",
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# IncreaseColorTemperature and DecreaseColorTemperature have their own
|
|
|
|
# tests
|
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_script(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test script discovery."""
|
2019-07-31 19:25:30 +00:00
|
|
|
device = ("script.test", "off", {"friendly_name": "Test script"})
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "script#test"
|
|
|
|
assert appliance["displayCategories"][0] == "ACTIVITY_TRIGGER"
|
|
|
|
assert appliance["friendlyName"] == "Test script"
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-11-26 07:05:10 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa.SceneController", "Alexa"
|
|
|
|
)
|
|
|
|
scene_capability = get_capability(capabilities, "Alexa.SceneController")
|
|
|
|
assert not scene_capability["supportsDeactivation"]
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
await assert_scene_controller_works("script#test", "script.turn_on", None, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_cancelable_script(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test cancalable script discovery."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"script.test_2",
|
|
|
|
"off",
|
|
|
|
{"friendly_name": "Test script 2", "can_cancel": True},
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "script#test_2"
|
2019-11-26 07:05:10 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa.SceneController", "Alexa"
|
|
|
|
)
|
|
|
|
scene_capability = get_capability(capabilities, "Alexa.SceneController")
|
|
|
|
assert scene_capability["supportsDeactivation"]
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_scene_controller_works(
|
2019-07-31 19:25:30 +00:00
|
|
|
"script#test_2", "script.turn_on", "script.turn_off", hass
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_input_boolean(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test input boolean discovery."""
|
2019-07-31 19:25:30 +00:00
|
|
|
device = ("input_boolean.test", "off", {"friendly_name": "Test input boolean"})
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "input_boolean#test"
|
|
|
|
assert appliance["displayCategories"][0] == "OTHER"
|
|
|
|
assert appliance["friendlyName"] == "Test input boolean"
|
2019-01-10 23:52:21 +00:00
|
|
|
assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_power_controller_works(
|
2019-07-31 19:25:30 +00:00
|
|
|
"input_boolean#test", "input_boolean.turn_on", "input_boolean.turn_off", hass
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_scene(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test scene discovery."""
|
2019-07-31 19:25:30 +00:00
|
|
|
device = ("scene.test", "off", {"friendly_name": "Test scene"})
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "scene#test"
|
|
|
|
assert appliance["displayCategories"][0] == "SCENE_TRIGGER"
|
|
|
|
assert appliance["friendlyName"] == "Test scene"
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-11-26 07:05:10 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa.SceneController", "Alexa"
|
|
|
|
)
|
|
|
|
scene_capability = get_capability(capabilities, "Alexa.SceneController")
|
|
|
|
assert not scene_capability["supportsDeactivation"]
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
await assert_scene_controller_works("scene#test", "scene.turn_on", None, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_fan(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test fan discovery."""
|
2019-07-31 19:25:30 +00:00
|
|
|
device = ("fan.test_1", "off", {"friendly_name": "Test fan 1"})
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "fan#test_1"
|
2019-10-02 22:55:01 +00:00
|
|
|
assert appliance["displayCategories"][0] == "FAN"
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["friendlyName"] == "Test fan 1"
|
2019-10-23 05:01:03 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-10-23 05:01:03 +00:00
|
|
|
power_capability = get_capability(capabilities, "Alexa.PowerController")
|
|
|
|
assert "capabilityResources" not in power_capability
|
|
|
|
assert "configuration" not in power_capability
|
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_variable_fan(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test fan discovery.
|
|
|
|
|
|
|
|
This one has variable speed.
|
|
|
|
"""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"fan.test_2",
|
|
|
|
"off",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test fan 2",
|
|
|
|
"supported_features": 1,
|
|
|
|
"speed_list": ["low", "medium", "high"],
|
|
|
|
"speed": "high",
|
|
|
|
},
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "fan#test_2"
|
2019-10-02 22:55:01 +00:00
|
|
|
assert appliance["displayCategories"][0] == "FAN"
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["friendlyName"] == "Test fan 2"
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-10-23 05:01:03 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
2018-01-30 04:33:39 +00:00
|
|
|
appliance,
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PercentageController",
|
|
|
|
"Alexa.PowerController",
|
2019-10-03 20:28:02 +00:00
|
|
|
"Alexa.PowerLevelController",
|
2019-10-23 05:01:03 +00:00
|
|
|
"Alexa.RangeController",
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.EndpointHealth",
|
2019-11-26 07:05:10 +00:00
|
|
|
"Alexa",
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
|
|
|
|
2019-10-23 05:01:03 +00:00
|
|
|
range_capability = get_capability(capabilities, "Alexa.RangeController")
|
|
|
|
assert range_capability is not None
|
|
|
|
assert range_capability["instance"] == "fan.speed"
|
|
|
|
|
|
|
|
properties = range_capability["properties"]
|
|
|
|
assert properties["nonControllable"] is False
|
|
|
|
assert {"name": "rangeValue"} in properties["supported"]
|
|
|
|
|
|
|
|
capability_resources = range_capability["capabilityResources"]
|
|
|
|
assert capability_resources is not None
|
|
|
|
assert {
|
|
|
|
"@type": "asset",
|
|
|
|
"value": {"assetId": "Alexa.Setting.FanSpeed"},
|
|
|
|
} in capability_resources["friendlyNames"]
|
|
|
|
|
|
|
|
configuration = range_capability["configuration"]
|
|
|
|
assert configuration is not None
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PercentageController",
|
|
|
|
"SetPercentage",
|
|
|
|
"fan#test_2",
|
|
|
|
"fan.set_speed",
|
2018-01-30 04:33:39 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"percentage": "50"},
|
|
|
|
)
|
|
|
|
assert call.data["speed"] == "medium"
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.PercentageController",
|
|
|
|
"SetPercentage",
|
|
|
|
"fan#test_2",
|
|
|
|
"fan.set_speed",
|
|
|
|
hass,
|
|
|
|
payload={"percentage": "33"},
|
|
|
|
)
|
|
|
|
assert call.data["speed"] == "low"
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.PercentageController",
|
|
|
|
"SetPercentage",
|
|
|
|
"fan#test_2",
|
|
|
|
"fan.set_speed",
|
|
|
|
hass,
|
|
|
|
payload={"percentage": "100"},
|
|
|
|
)
|
|
|
|
assert call.data["speed"] == "high"
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_percentage_changes(
|
2018-01-30 04:33:39 +00:00
|
|
|
hass,
|
2019-12-19 11:44:17 +00:00
|
|
|
[("high", "-5"), ("off", "5"), ("low", "-80"), ("medium", "-34")],
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PercentageController",
|
|
|
|
"AdjustPercentage",
|
|
|
|
"fan#test_2",
|
|
|
|
"percentageDelta",
|
|
|
|
"fan.set_speed",
|
|
|
|
"speed",
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.PowerLevelController",
|
|
|
|
"SetPowerLevel",
|
|
|
|
"fan#test_2",
|
|
|
|
"fan.set_speed",
|
|
|
|
hass,
|
|
|
|
payload={"powerLevel": "20"},
|
|
|
|
)
|
|
|
|
assert call.data["speed"] == "low"
|
|
|
|
|
2019-10-03 20:28:02 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.PowerLevelController",
|
|
|
|
"SetPowerLevel",
|
|
|
|
"fan#test_2",
|
|
|
|
"fan.set_speed",
|
|
|
|
hass,
|
|
|
|
payload={"powerLevel": "50"},
|
|
|
|
)
|
|
|
|
assert call.data["speed"] == "medium"
|
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.PowerLevelController",
|
|
|
|
"SetPowerLevel",
|
|
|
|
"fan#test_2",
|
|
|
|
"fan.set_speed",
|
|
|
|
hass,
|
|
|
|
payload={"powerLevel": "99"},
|
|
|
|
)
|
|
|
|
assert call.data["speed"] == "high"
|
|
|
|
|
2019-10-03 20:28:02 +00:00
|
|
|
await assert_percentage_changes(
|
|
|
|
hass,
|
2019-10-23 05:01:03 +00:00
|
|
|
[("high", "-5"), ("medium", "-50"), ("low", "-80")],
|
2019-10-03 20:28:02 +00:00
|
|
|
"Alexa.PowerLevelController",
|
|
|
|
"AdjustPowerLevel",
|
|
|
|
"fan#test_2",
|
|
|
|
"powerLevelDelta",
|
|
|
|
"fan.set_speed",
|
|
|
|
"speed",
|
|
|
|
)
|
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-10-23 05:01:03 +00:00
|
|
|
async def test_oscillating_fan(hass):
|
|
|
|
"""Test oscillating fan discovery."""
|
|
|
|
device = (
|
|
|
|
"fan.test_3",
|
|
|
|
"off",
|
|
|
|
{"friendly_name": "Test fan 3", "supported_features": 3},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "fan#test_3"
|
|
|
|
assert appliance["displayCategories"][0] == "FAN"
|
|
|
|
assert appliance["friendlyName"] == "Test fan 3"
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance,
|
|
|
|
"Alexa.PercentageController",
|
|
|
|
"Alexa.PowerController",
|
|
|
|
"Alexa.PowerLevelController",
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"Alexa.ToggleController",
|
|
|
|
"Alexa.EndpointHealth",
|
2019-11-26 07:05:10 +00:00
|
|
|
"Alexa",
|
2019-10-23 05:01:03 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
toggle_capability = get_capability(capabilities, "Alexa.ToggleController")
|
|
|
|
assert toggle_capability is not None
|
|
|
|
assert toggle_capability["instance"] == "fan.oscillating"
|
|
|
|
|
|
|
|
properties = toggle_capability["properties"]
|
|
|
|
assert properties["nonControllable"] is False
|
|
|
|
assert {"name": "toggleState"} in properties["supported"]
|
|
|
|
|
|
|
|
capability_resources = toggle_capability["capabilityResources"]
|
|
|
|
assert capability_resources is not None
|
|
|
|
assert {
|
|
|
|
"@type": "asset",
|
|
|
|
"value": {"assetId": "Alexa.Setting.Oscillate"},
|
|
|
|
} in capability_resources["friendlyNames"]
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.ToggleController",
|
|
|
|
"TurnOn",
|
|
|
|
"fan#test_3",
|
|
|
|
"fan.oscillate",
|
|
|
|
hass,
|
|
|
|
payload={},
|
|
|
|
instance="fan.oscillating",
|
|
|
|
)
|
|
|
|
assert call.data["oscillating"]
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.ToggleController",
|
|
|
|
"TurnOff",
|
|
|
|
"fan#test_3",
|
|
|
|
"fan.oscillate",
|
|
|
|
hass,
|
|
|
|
payload={},
|
|
|
|
instance="fan.oscillating",
|
|
|
|
)
|
|
|
|
assert not call.data["oscillating"]
|
|
|
|
|
|
|
|
|
|
|
|
async def test_direction_fan(hass):
|
|
|
|
"""Test direction fan discovery."""
|
|
|
|
device = (
|
|
|
|
"fan.test_4",
|
|
|
|
"on",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test fan 4",
|
|
|
|
"supported_features": 5,
|
|
|
|
"direction": "forward",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "fan#test_4"
|
|
|
|
assert appliance["displayCategories"][0] == "FAN"
|
|
|
|
assert appliance["friendlyName"] == "Test fan 4"
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance,
|
|
|
|
"Alexa.PercentageController",
|
|
|
|
"Alexa.PowerController",
|
|
|
|
"Alexa.PowerLevelController",
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"Alexa.ModeController",
|
|
|
|
"Alexa.EndpointHealth",
|
2019-11-26 07:05:10 +00:00
|
|
|
"Alexa",
|
2019-10-23 05:01:03 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
mode_capability = get_capability(capabilities, "Alexa.ModeController")
|
|
|
|
assert mode_capability is not None
|
|
|
|
assert mode_capability["instance"] == "fan.direction"
|
|
|
|
|
|
|
|
properties = mode_capability["properties"]
|
|
|
|
assert properties["nonControllable"] is False
|
|
|
|
assert {"name": "mode"} in properties["supported"]
|
|
|
|
|
|
|
|
capability_resources = mode_capability["capabilityResources"]
|
|
|
|
assert capability_resources is not None
|
|
|
|
assert {
|
|
|
|
"@type": "asset",
|
|
|
|
"value": {"assetId": "Alexa.Setting.Direction"},
|
|
|
|
} in capability_resources["friendlyNames"]
|
|
|
|
|
|
|
|
configuration = mode_capability["configuration"]
|
|
|
|
assert configuration is not None
|
|
|
|
assert configuration["ordered"] is False
|
|
|
|
|
|
|
|
supported_modes = configuration["supportedModes"]
|
|
|
|
assert supported_modes is not None
|
|
|
|
assert {
|
|
|
|
"value": "direction.forward",
|
|
|
|
"modeResources": {
|
|
|
|
"friendlyNames": [
|
|
|
|
{"@type": "text", "value": {"text": "forward", "locale": "en-US"}}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
} in supported_modes
|
|
|
|
assert {
|
|
|
|
"value": "direction.reverse",
|
|
|
|
"modeResources": {
|
|
|
|
"friendlyNames": [
|
|
|
|
{"@type": "text", "value": {"text": "reverse", "locale": "en-US"}}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
} in supported_modes
|
|
|
|
|
2019-11-25 23:07:33 +00:00
|
|
|
call, msg = await assert_request_calls_service(
|
2019-10-23 05:01:03 +00:00
|
|
|
"Alexa.ModeController",
|
|
|
|
"SetMode",
|
|
|
|
"fan#test_4",
|
|
|
|
"fan.set_direction",
|
|
|
|
hass,
|
|
|
|
payload={"mode": "direction.reverse"},
|
|
|
|
instance="fan.direction",
|
|
|
|
)
|
|
|
|
assert call.data["direction"] == "reverse"
|
2019-11-25 23:07:33 +00:00
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "mode"
|
|
|
|
assert properties["namespace"] == "Alexa.ModeController"
|
|
|
|
assert properties["value"] == "direction.reverse"
|
|
|
|
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.ModeController",
|
|
|
|
"SetMode",
|
|
|
|
"fan#test_4",
|
|
|
|
"fan.set_direction",
|
|
|
|
hass,
|
|
|
|
payload={"mode": "direction.forward"},
|
|
|
|
instance="fan.direction",
|
|
|
|
)
|
|
|
|
assert call.data["direction"] == "forward"
|
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "mode"
|
|
|
|
assert properties["namespace"] == "Alexa.ModeController"
|
|
|
|
assert properties["value"] == "direction.forward"
|
2019-10-23 05:01:03 +00:00
|
|
|
|
|
|
|
# Test for AdjustMode instance=None Error coverage
|
|
|
|
with pytest.raises(AssertionError):
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.ModeController",
|
|
|
|
"AdjustMode",
|
|
|
|
"fan#test_4",
|
|
|
|
"fan.set_direction",
|
|
|
|
hass,
|
|
|
|
payload={},
|
|
|
|
instance=None,
|
|
|
|
)
|
|
|
|
assert call.data
|
|
|
|
|
|
|
|
|
|
|
|
async def test_fan_range(hass):
|
|
|
|
"""Test fan discovery with range controller.
|
|
|
|
|
|
|
|
This one has variable speed.
|
|
|
|
"""
|
|
|
|
device = (
|
|
|
|
"fan.test_5",
|
|
|
|
"off",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test fan 5",
|
|
|
|
"supported_features": 1,
|
|
|
|
"speed_list": ["low", "medium", "high"],
|
|
|
|
"speed": "medium",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "fan#test_5"
|
|
|
|
assert appliance["displayCategories"][0] == "FAN"
|
|
|
|
assert appliance["friendlyName"] == "Test fan 5"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance,
|
|
|
|
"Alexa.PercentageController",
|
|
|
|
"Alexa.PowerController",
|
|
|
|
"Alexa.PowerLevelController",
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"Alexa.EndpointHealth",
|
2019-11-26 07:05:10 +00:00
|
|
|
"Alexa",
|
2019-10-23 05:01:03 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
range_capability = get_capability(capabilities, "Alexa.RangeController")
|
|
|
|
assert range_capability is not None
|
|
|
|
assert range_capability["instance"] == "fan.speed"
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"SetRangeValue",
|
|
|
|
"fan#test_5",
|
|
|
|
"fan.set_speed",
|
|
|
|
hass,
|
|
|
|
payload={"rangeValue": "1"},
|
|
|
|
instance="fan.speed",
|
|
|
|
)
|
|
|
|
assert call.data["speed"] == "low"
|
|
|
|
|
|
|
|
await assert_range_changes(
|
|
|
|
hass,
|
|
|
|
[("low", "-1"), ("high", "1"), ("medium", "0")],
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"AdjustRangeValue",
|
|
|
|
"fan#test_5",
|
|
|
|
False,
|
|
|
|
"fan.set_speed",
|
|
|
|
"speed",
|
|
|
|
instance="fan.speed",
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_fan_range_off(hass):
|
|
|
|
"""Test fan range controller 0 turns_off fan."""
|
|
|
|
device = (
|
|
|
|
"fan.test_6",
|
|
|
|
"off",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test fan 6",
|
|
|
|
"supported_features": 1,
|
|
|
|
"speed_list": ["low", "medium", "high"],
|
|
|
|
"speed": "high",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
await discovery_test(device, hass)
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"SetRangeValue",
|
|
|
|
"fan#test_6",
|
|
|
|
"fan.turn_off",
|
|
|
|
hass,
|
|
|
|
payload={"rangeValue": "0"},
|
|
|
|
instance="fan.speed",
|
|
|
|
)
|
|
|
|
assert call.data["speed"] == "off"
|
|
|
|
|
|
|
|
await assert_range_changes(
|
|
|
|
hass,
|
|
|
|
[("off", "-3")],
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"AdjustRangeValue",
|
|
|
|
"fan#test_6",
|
|
|
|
False,
|
|
|
|
"fan.turn_off",
|
|
|
|
"speed",
|
|
|
|
instance="fan.speed",
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_lock(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test lock discovery."""
|
2019-07-31 19:25:30 +00:00
|
|
|
device = ("lock.test", "off", {"friendly_name": "Test lock"})
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "lock#test"
|
|
|
|
assert appliance["displayCategories"][0] == "SMARTLOCK"
|
|
|
|
assert appliance["friendlyName"] == "Test lock"
|
2019-01-10 23:52:21 +00:00
|
|
|
assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.LockController", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
_, msg = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.LockController", "Lock", "lock#test", "lock.lock", hass
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "lockState"
|
|
|
|
assert properties["namespace"] == "Alexa.LockController"
|
|
|
|
assert properties["value"] == "LOCKED"
|
2018-02-12 07:20:54 +00:00
|
|
|
|
2019-10-14 21:19:05 +00:00
|
|
|
_, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.LockController", "Unlock", "lock#test", "lock.unlock", hass
|
|
|
|
)
|
|
|
|
|
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "lockState"
|
|
|
|
assert properties["namespace"] == "Alexa.LockController"
|
|
|
|
assert properties["value"] == "UNLOCKED"
|
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_media_player(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test media player discovery."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"media_player.test",
|
|
|
|
"off",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test media player",
|
2019-10-23 15:28:23 +00:00
|
|
|
"supported_features": SUPPORT_NEXT_TRACK
|
|
|
|
| SUPPORT_PAUSE
|
|
|
|
| SUPPORT_PLAY
|
|
|
|
| SUPPORT_PLAY_MEDIA
|
|
|
|
| SUPPORT_PREVIOUS_TRACK
|
|
|
|
| SUPPORT_SELECT_SOURCE
|
|
|
|
| SUPPORT_STOP
|
|
|
|
| SUPPORT_TURN_OFF
|
|
|
|
| SUPPORT_TURN_ON
|
|
|
|
| SUPPORT_VOLUME_MUTE
|
|
|
|
| SUPPORT_VOLUME_SET,
|
2019-07-31 19:25:30 +00:00
|
|
|
"volume_level": 0.75,
|
|
|
|
},
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "media_player#test"
|
|
|
|
assert appliance["displayCategories"][0] == "TV"
|
|
|
|
assert appliance["friendlyName"] == "Test media player"
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-10-25 20:34:52 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
2018-01-30 04:33:39 +00:00
|
|
|
appliance,
|
2019-11-26 07:05:10 +00:00
|
|
|
"Alexa",
|
2019-10-31 09:38:44 +00:00
|
|
|
"Alexa.ChannelController",
|
|
|
|
"Alexa.EndpointHealth",
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.InputController",
|
2019-10-31 09:38:44 +00:00
|
|
|
"Alexa.PlaybackController",
|
|
|
|
"Alexa.PlaybackStateReporter",
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PowerController",
|
|
|
|
"Alexa.Speaker",
|
|
|
|
"Alexa.StepSpeaker",
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
|
|
|
|
2019-10-25 20:34:52 +00:00
|
|
|
playback_capability = get_capability(capabilities, "Alexa.PlaybackController")
|
|
|
|
assert playback_capability is not None
|
|
|
|
supported_operations = playback_capability["supportedOperations"]
|
|
|
|
operations = ["Play", "Pause", "Stop", "Next", "Previous"]
|
|
|
|
for operation in operations:
|
|
|
|
assert operation in supported_operations
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_power_controller_works(
|
2019-07-31 19:25:30 +00:00
|
|
|
"media_player#test", "media_player.turn_on", "media_player.turn_off", hass
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PlaybackController",
|
|
|
|
"Play",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.media_play",
|
|
|
|
hass,
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PlaybackController",
|
|
|
|
"Pause",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.media_pause",
|
|
|
|
hass,
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PlaybackController",
|
|
|
|
"Stop",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.media_stop",
|
|
|
|
hass,
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PlaybackController",
|
|
|
|
"Next",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.media_next_track",
|
|
|
|
hass,
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PlaybackController",
|
|
|
|
"Previous",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.media_previous_track",
|
|
|
|
hass,
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.Speaker",
|
|
|
|
"SetVolume",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.volume_set",
|
2018-01-30 04:33:39 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"volume": 50},
|
|
|
|
)
|
|
|
|
assert call.data["volume_level"] == 0.5
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.Speaker",
|
|
|
|
"SetMute",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.volume_mute",
|
2018-01-30 04:33:39 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"mute": True},
|
|
|
|
)
|
|
|
|
assert call.data["is_volume_muted"]
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _, = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.Speaker",
|
|
|
|
"SetMute",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.volume_mute",
|
2018-01-30 04:33:39 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"mute": False},
|
|
|
|
)
|
|
|
|
assert not call.data["is_volume_muted"]
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_percentage_changes(
|
2018-01-30 04:33:39 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
[(0.7, "-5"), (0.8, "5"), (0, "-80")],
|
|
|
|
"Alexa.Speaker",
|
|
|
|
"AdjustVolume",
|
|
|
|
"media_player#test",
|
|
|
|
"volume",
|
|
|
|
"media_player.volume_set",
|
|
|
|
"volume_level",
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.StepSpeaker",
|
|
|
|
"SetMute",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.volume_mute",
|
2018-02-06 00:02:08 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"mute": True},
|
|
|
|
)
|
|
|
|
assert call.data["is_volume_muted"]
|
2018-02-06 00:02:08 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _, = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.StepSpeaker",
|
|
|
|
"SetMute",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.volume_mute",
|
2018-02-06 00:02:08 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"mute": False},
|
|
|
|
)
|
|
|
|
assert not call.data["is_volume_muted"]
|
2018-02-06 00:02:08 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.StepSpeaker",
|
|
|
|
"AdjustVolume",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.volume_up",
|
2018-02-06 00:02:08 +00:00
|
|
|
hass,
|
2019-10-23 15:28:23 +00:00
|
|
|
payload={"volumeSteps": 1, "volumeStepsDefault": False},
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2018-02-06 00:02:08 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.StepSpeaker",
|
|
|
|
"AdjustVolume",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.volume_down",
|
2018-02-06 00:02:08 +00:00
|
|
|
hass,
|
2019-10-23 15:28:23 +00:00
|
|
|
payload={"volumeSteps": -1, "volumeStepsDefault": False},
|
|
|
|
)
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.StepSpeaker",
|
|
|
|
"AdjustVolume",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.volume_up",
|
|
|
|
hass,
|
|
|
|
payload={"volumeSteps": 10, "volumeStepsDefault": True},
|
|
|
|
)
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.ChannelController",
|
|
|
|
"ChangeChannel",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.play_media",
|
|
|
|
hass,
|
2019-12-03 00:10:44 +00:00
|
|
|
payload={"channel": {"number": "24"}, "channelMetadata": {"name": ""}},
|
2019-10-23 15:28:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.ChannelController",
|
|
|
|
"ChangeChannel",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.play_media",
|
|
|
|
hass,
|
2019-12-03 00:10:44 +00:00
|
|
|
payload={"channel": {"callSign": "ABC"}, "channelMetadata": {"name": ""}},
|
2019-10-23 15:28:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.ChannelController",
|
|
|
|
"ChangeChannel",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.play_media",
|
|
|
|
hass,
|
2019-12-03 00:10:44 +00:00
|
|
|
payload={"channel": {"number": ""}, "channelMetadata": {"name": "ABC"}},
|
2019-10-23 15:28:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.ChannelController",
|
|
|
|
"ChangeChannel",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.play_media",
|
|
|
|
hass,
|
2019-12-03 00:10:44 +00:00
|
|
|
payload={
|
|
|
|
"channel": {"affiliateCallSign": "ABC"},
|
|
|
|
"channelMetadata": {"name": ""},
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.ChannelController",
|
|
|
|
"ChangeChannel",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.play_media",
|
|
|
|
hass,
|
|
|
|
payload={"channel": {"uri": "ABC"}, "channelMetadata": {"name": ""}},
|
2019-10-23 15:28:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.ChannelController",
|
|
|
|
"SkipChannels",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.media_next_track",
|
|
|
|
hass,
|
|
|
|
payload={"channelCount": 1},
|
|
|
|
)
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.ChannelController",
|
|
|
|
"SkipChannels",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.media_previous_track",
|
|
|
|
hass,
|
|
|
|
payload={"channelCount": -1},
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2018-02-06 00:02:08 +00:00
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-04-29 20:40:55 +00:00
|
|
|
async def test_media_player_power(hass):
|
|
|
|
"""Test media player discovery with mapped on/off."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"media_player.test",
|
|
|
|
"off",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test media player",
|
|
|
|
"supported_features": 0xFA3F,
|
|
|
|
"volume_level": 0.75,
|
|
|
|
},
|
2019-04-29 20:40:55 +00:00
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "media_player#test"
|
|
|
|
assert appliance["displayCategories"][0] == "TV"
|
|
|
|
assert appliance["friendlyName"] == "Test media player"
|
2019-04-29 20:40:55 +00:00
|
|
|
|
|
|
|
assert_endpoint_capabilities(
|
|
|
|
appliance,
|
2019-11-26 07:05:10 +00:00
|
|
|
"Alexa",
|
2019-10-31 09:38:44 +00:00
|
|
|
"Alexa.ChannelController",
|
|
|
|
"Alexa.EndpointHealth",
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.InputController",
|
2019-10-31 09:38:44 +00:00
|
|
|
"Alexa.PlaybackController",
|
|
|
|
"Alexa.PlaybackStateReporter",
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PowerController",
|
2019-10-31 09:38:44 +00:00
|
|
|
"Alexa.SeekController",
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.Speaker",
|
|
|
|
"Alexa.StepSpeaker",
|
2019-04-29 20:40:55 +00:00
|
|
|
)
|
|
|
|
|
2019-07-11 15:35:46 +00:00
|
|
|
await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PowerController",
|
|
|
|
"TurnOn",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.media_play",
|
|
|
|
hass,
|
|
|
|
)
|
2019-07-11 15:35:46 +00:00
|
|
|
|
|
|
|
await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PowerController",
|
|
|
|
"TurnOff",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.media_stop",
|
|
|
|
hass,
|
|
|
|
)
|
2019-07-11 15:35:46 +00:00
|
|
|
|
2019-04-29 20:40:55 +00:00
|
|
|
|
2019-11-25 23:17:12 +00:00
|
|
|
async def test_media_player_inputs(hass):
|
|
|
|
"""Test media player discovery with source list inputs."""
|
|
|
|
device = (
|
|
|
|
"media_player.test",
|
|
|
|
"on",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test media player",
|
|
|
|
"supported_features": SUPPORT_SELECT_SOURCE,
|
|
|
|
"volume_level": 0.75,
|
|
|
|
"source_list": [
|
|
|
|
"foo",
|
|
|
|
"foo_2",
|
|
|
|
"hdmi",
|
|
|
|
"hdmi_2",
|
|
|
|
"hdmi-3",
|
|
|
|
"hdmi4",
|
|
|
|
"hdmi 5",
|
|
|
|
"HDMI 6",
|
|
|
|
"hdmi_arc",
|
|
|
|
"aux",
|
|
|
|
"input 1",
|
|
|
|
"tv",
|
|
|
|
],
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "media_player#test"
|
|
|
|
assert appliance["displayCategories"][0] == "TV"
|
|
|
|
assert appliance["friendlyName"] == "Test media player"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance,
|
2019-11-26 15:17:41 +00:00
|
|
|
"Alexa",
|
2019-11-25 23:17:12 +00:00
|
|
|
"Alexa.InputController",
|
|
|
|
"Alexa.PowerController",
|
|
|
|
"Alexa.EndpointHealth",
|
|
|
|
)
|
|
|
|
|
|
|
|
input_capability = get_capability(capabilities, "Alexa.InputController")
|
|
|
|
assert input_capability is not None
|
|
|
|
assert {"name": "AUX"} not in input_capability["inputs"]
|
|
|
|
assert {"name": "AUX 1"} in input_capability["inputs"]
|
|
|
|
assert {"name": "HDMI 1"} in input_capability["inputs"]
|
|
|
|
assert {"name": "HDMI 2"} in input_capability["inputs"]
|
|
|
|
assert {"name": "HDMI 3"} in input_capability["inputs"]
|
|
|
|
assert {"name": "HDMI 4"} in input_capability["inputs"]
|
|
|
|
assert {"name": "HDMI 5"} in input_capability["inputs"]
|
|
|
|
assert {"name": "HDMI 6"} in input_capability["inputs"]
|
|
|
|
assert {"name": "HDMI ARC"} in input_capability["inputs"]
|
|
|
|
assert {"name": "FOO 1"} not in input_capability["inputs"]
|
|
|
|
assert {"name": "TV"} in input_capability["inputs"]
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.InputController",
|
|
|
|
"SelectInput",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.select_source",
|
|
|
|
hass,
|
|
|
|
payload={"input": "HDMI 1"},
|
|
|
|
)
|
|
|
|
assert call.data["source"] == "hdmi"
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.InputController",
|
|
|
|
"SelectInput",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.select_source",
|
|
|
|
hass,
|
|
|
|
payload={"input": "HDMI 2"},
|
|
|
|
)
|
|
|
|
assert call.data["source"] == "hdmi_2"
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.InputController",
|
|
|
|
"SelectInput",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.select_source",
|
|
|
|
hass,
|
|
|
|
payload={"input": "HDMI 5"},
|
|
|
|
)
|
|
|
|
assert call.data["source"] == "hdmi 5"
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.InputController",
|
|
|
|
"SelectInput",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.select_source",
|
|
|
|
hass,
|
|
|
|
payload={"input": "HDMI 6"},
|
|
|
|
)
|
|
|
|
assert call.data["source"] == "HDMI 6"
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.InputController",
|
|
|
|
"SelectInput",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.select_source",
|
|
|
|
hass,
|
|
|
|
payload={"input": "TV"},
|
|
|
|
)
|
|
|
|
assert call.data["source"] == "tv"
|
|
|
|
|
|
|
|
|
2019-10-25 17:21:22 +00:00
|
|
|
async def test_media_player_speaker(hass):
|
|
|
|
"""Test media player discovery with device class speaker."""
|
|
|
|
device = (
|
|
|
|
"media_player.test",
|
|
|
|
"off",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test media player",
|
|
|
|
"supported_features": 51765,
|
|
|
|
"volume_level": 0.75,
|
|
|
|
"device_class": "speaker",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "media_player#test"
|
|
|
|
assert appliance["displayCategories"][0] == "SPEAKER"
|
|
|
|
assert appliance["friendlyName"] == "Test media player"
|
|
|
|
|
|
|
|
|
2019-10-31 09:38:44 +00:00
|
|
|
async def test_media_player_seek(hass):
|
|
|
|
"""Test media player seek capability."""
|
|
|
|
device = (
|
|
|
|
"media_player.test_seek",
|
|
|
|
"playing",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test media player seek",
|
|
|
|
"supported_features": SUPPORT_SEEK,
|
|
|
|
"media_position": 300, # 5min
|
|
|
|
"media_duration": 600, # 10min
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "media_player#test_seek"
|
|
|
|
assert appliance["displayCategories"][0] == "TV"
|
|
|
|
assert appliance["friendlyName"] == "Test media player seek"
|
|
|
|
|
|
|
|
assert_endpoint_capabilities(
|
|
|
|
appliance,
|
2019-11-26 07:05:10 +00:00
|
|
|
"Alexa",
|
2019-10-31 09:38:44 +00:00
|
|
|
"Alexa.EndpointHealth",
|
|
|
|
"Alexa.PowerController",
|
|
|
|
"Alexa.SeekController",
|
|
|
|
)
|
|
|
|
|
|
|
|
# Test seek forward 30 seconds.
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.SeekController",
|
|
|
|
"AdjustSeekPosition",
|
|
|
|
"media_player#test_seek",
|
|
|
|
"media_player.media_seek",
|
|
|
|
hass,
|
|
|
|
response_type="StateReport",
|
|
|
|
payload={"deltaPositionMilliseconds": 30000},
|
|
|
|
)
|
|
|
|
assert call.data["seek_position"] == 330
|
|
|
|
assert "properties" in msg["event"]["payload"]
|
|
|
|
properties = msg["event"]["payload"]["properties"]
|
|
|
|
assert {"name": "positionMilliseconds", "value": 330000} in properties
|
|
|
|
|
|
|
|
# Test seek reverse 30 seconds.
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.SeekController",
|
|
|
|
"AdjustSeekPosition",
|
|
|
|
"media_player#test_seek",
|
|
|
|
"media_player.media_seek",
|
|
|
|
hass,
|
|
|
|
response_type="StateReport",
|
|
|
|
payload={"deltaPositionMilliseconds": -30000},
|
|
|
|
)
|
|
|
|
assert call.data["seek_position"] == 270
|
|
|
|
assert "properties" in msg["event"]["payload"]
|
|
|
|
properties = msg["event"]["payload"]["properties"]
|
|
|
|
assert {"name": "positionMilliseconds", "value": 270000} in properties
|
|
|
|
|
|
|
|
# Test seek backwards more than current position (5 min.) result = 0.
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.SeekController",
|
|
|
|
"AdjustSeekPosition",
|
|
|
|
"media_player#test_seek",
|
|
|
|
"media_player.media_seek",
|
|
|
|
hass,
|
|
|
|
response_type="StateReport",
|
|
|
|
payload={"deltaPositionMilliseconds": -500000},
|
|
|
|
)
|
|
|
|
assert call.data["seek_position"] == 0
|
|
|
|
assert "properties" in msg["event"]["payload"]
|
|
|
|
properties = msg["event"]["payload"]["properties"]
|
|
|
|
assert {"name": "positionMilliseconds", "value": 0} in properties
|
|
|
|
|
|
|
|
# Test seek forward more than current duration (10 min.) result = 600 sec.
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.SeekController",
|
|
|
|
"AdjustSeekPosition",
|
|
|
|
"media_player#test_seek",
|
|
|
|
"media_player.media_seek",
|
|
|
|
hass,
|
|
|
|
response_type="StateReport",
|
|
|
|
payload={"deltaPositionMilliseconds": 800000},
|
|
|
|
)
|
|
|
|
assert call.data["seek_position"] == 600
|
|
|
|
assert "properties" in msg["event"]["payload"]
|
|
|
|
properties = msg["event"]["payload"]["properties"]
|
|
|
|
assert {"name": "positionMilliseconds", "value": 600000} in properties
|
|
|
|
|
|
|
|
|
|
|
|
async def test_media_player_seek_error(hass):
|
|
|
|
"""Test media player seek capability for media_position Error."""
|
|
|
|
device = (
|
|
|
|
"media_player.test_seek",
|
|
|
|
"playing",
|
|
|
|
{"friendly_name": "Test media player seek", "supported_features": SUPPORT_SEEK},
|
|
|
|
)
|
|
|
|
await discovery_test(device, hass)
|
|
|
|
|
|
|
|
# Test for media_position error.
|
|
|
|
with pytest.raises(AssertionError):
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.SeekController",
|
|
|
|
"AdjustSeekPosition",
|
|
|
|
"media_player#test_seek",
|
|
|
|
"media_player.media_seek",
|
|
|
|
hass,
|
|
|
|
response_type="StateReport",
|
|
|
|
payload={"deltaPositionMilliseconds": 30000},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
|
|
|
assert msg["header"]["name"] == "ErrorResponse"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa.Video"
|
|
|
|
assert msg["payload"]["type"] == "ACTION_NOT_PERMITTED_FOR_CONTENT"
|
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_alert(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test alert discovery."""
|
2019-07-31 19:25:30 +00:00
|
|
|
device = ("alert.test", "off", {"friendly_name": "Test alert"})
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "alert#test"
|
|
|
|
assert appliance["displayCategories"][0] == "OTHER"
|
|
|
|
assert appliance["friendlyName"] == "Test alert"
|
2019-01-10 23:52:21 +00:00
|
|
|
assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_power_controller_works(
|
2019-07-31 19:25:30 +00:00
|
|
|
"alert#test", "alert.turn_on", "alert.turn_off", hass
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_automation(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test automation discovery."""
|
2019-07-31 19:25:30 +00:00
|
|
|
device = ("automation.test", "off", {"friendly_name": "Test automation"})
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "automation#test"
|
|
|
|
assert appliance["displayCategories"][0] == "OTHER"
|
|
|
|
assert appliance["friendlyName"] == "Test automation"
|
2019-01-10 23:52:21 +00:00
|
|
|
assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_power_controller_works(
|
2019-07-31 19:25:30 +00:00
|
|
|
"automation#test", "automation.turn_on", "automation.turn_off", hass
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_group(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test group discovery."""
|
2019-07-31 19:25:30 +00:00
|
|
|
device = ("group.test", "off", {"friendly_name": "Test group"})
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "group#test"
|
|
|
|
assert appliance["displayCategories"][0] == "OTHER"
|
|
|
|
assert appliance["friendlyName"] == "Test group"
|
2019-01-10 23:52:21 +00:00
|
|
|
assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
await assert_power_controller_works(
|
2019-07-31 19:25:30 +00:00
|
|
|
"group#test", "homeassistant.turn_on", "homeassistant.turn_off", hass
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
async def test_cover_position_range(hass):
|
|
|
|
"""Test cover discovery and position using rangeController."""
|
2018-01-30 04:33:39 +00:00
|
|
|
device = (
|
2019-12-19 11:44:17 +00:00
|
|
|
"cover.test_range",
|
|
|
|
"open",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test cover range",
|
|
|
|
"device_class": "blind",
|
|
|
|
"supported_features": 7,
|
|
|
|
"position": 30,
|
|
|
|
},
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
assert appliance["endpointId"] == "cover#test_range"
|
|
|
|
assert appliance["displayCategories"][0] == "INTERIOR_BLIND"
|
|
|
|
assert appliance["friendlyName"] == "Test cover range"
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa.RangeController", "Alexa.EndpointHealth", "Alexa"
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
range_capability = get_capability(capabilities, "Alexa.RangeController")
|
|
|
|
assert range_capability is not None
|
|
|
|
assert range_capability["instance"] == "cover.position"
|
|
|
|
|
|
|
|
properties = range_capability["properties"]
|
|
|
|
assert properties["nonControllable"] is False
|
|
|
|
assert {"name": "rangeValue"} in properties["supported"]
|
|
|
|
|
|
|
|
capability_resources = range_capability["capabilityResources"]
|
|
|
|
assert capability_resources is not None
|
|
|
|
assert {
|
|
|
|
"@type": "text",
|
|
|
|
"value": {"text": "Position", "locale": "en-US"},
|
|
|
|
} in capability_resources["friendlyNames"]
|
|
|
|
|
|
|
|
assert {
|
|
|
|
"@type": "asset",
|
|
|
|
"value": {"assetId": "Alexa.Setting.Opening"},
|
|
|
|
} in capability_resources["friendlyNames"]
|
|
|
|
|
|
|
|
configuration = range_capability["configuration"]
|
|
|
|
assert configuration is not None
|
|
|
|
assert configuration["unitOfMeasure"] == "Alexa.Unit.Percent"
|
|
|
|
|
|
|
|
supported_range = configuration["supportedRange"]
|
|
|
|
assert supported_range["minimumValue"] == 0
|
|
|
|
assert supported_range["maximumValue"] == 100
|
|
|
|
assert supported_range["precision"] == 1
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
2019-12-19 11:44:17 +00:00
|
|
|
"Alexa.RangeController",
|
|
|
|
"SetRangeValue",
|
|
|
|
"cover#test_range",
|
2019-07-31 19:25:30 +00:00
|
|
|
"cover.set_cover_position",
|
2018-01-30 04:33:39 +00:00
|
|
|
hass,
|
2019-12-19 11:44:17 +00:00
|
|
|
payload={"rangeValue": "50"},
|
|
|
|
instance="cover.position",
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
assert call.data["position"] == 50
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"SetRangeValue",
|
|
|
|
"cover#test_range",
|
|
|
|
"cover.close_cover",
|
2018-01-30 04:33:39 +00:00
|
|
|
hass,
|
2019-12-19 11:44:17 +00:00
|
|
|
payload={"rangeValue": "0"},
|
|
|
|
instance="cover.position",
|
|
|
|
)
|
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "rangeValue"
|
|
|
|
assert properties["namespace"] == "Alexa.RangeController"
|
|
|
|
assert properties["value"] == 0
|
|
|
|
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"SetRangeValue",
|
|
|
|
"cover#test_range",
|
|
|
|
"cover.open_cover",
|
|
|
|
hass,
|
|
|
|
payload={"rangeValue": "100"},
|
|
|
|
instance="cover.position",
|
|
|
|
)
|
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "rangeValue"
|
|
|
|
assert properties["namespace"] == "Alexa.RangeController"
|
|
|
|
assert properties["value"] == 100
|
|
|
|
|
|
|
|
await assert_range_changes(
|
|
|
|
hass,
|
|
|
|
[(25, "-5"), (35, "5"), (0, "-99"), (100, "99")],
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"AdjustRangeValue",
|
|
|
|
"cover#test_range",
|
|
|
|
False,
|
2019-07-31 19:25:30 +00:00
|
|
|
"cover.set_cover_position",
|
|
|
|
"position",
|
2019-12-19 11:44:17 +00:00
|
|
|
instance="cover.position",
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def assert_percentage_changes(
|
2019-07-31 19:25:30 +00:00
|
|
|
hass, adjustments, namespace, name, endpoint, parameter, service, changed_parameter
|
|
|
|
):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Assert an API request making percentage changes works.
|
|
|
|
|
|
|
|
AdjustPercentage, AdjustBrightness, etc. are examples of such requests.
|
|
|
|
"""
|
|
|
|
for result_volume, adjustment in adjustments:
|
|
|
|
if parameter:
|
|
|
|
payload = {parameter: adjustment}
|
|
|
|
else:
|
|
|
|
payload = {}
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
namespace, name, endpoint, service, hass, payload=payload
|
|
|
|
)
|
2018-01-30 04:33:39 +00:00
|
|
|
assert call.data[changed_parameter] == result_volume
|
|
|
|
|
|
|
|
|
2019-10-23 05:01:03 +00:00
|
|
|
async def assert_range_changes(
|
|
|
|
hass,
|
|
|
|
adjustments,
|
|
|
|
namespace,
|
|
|
|
name,
|
|
|
|
endpoint,
|
|
|
|
delta_default,
|
|
|
|
service,
|
|
|
|
changed_parameter,
|
|
|
|
instance,
|
|
|
|
):
|
|
|
|
"""Assert an API request making range changes works.
|
|
|
|
|
|
|
|
AdjustRangeValue are examples of such requests.
|
|
|
|
"""
|
|
|
|
for result_range, adjustment in adjustments:
|
|
|
|
payload = {
|
|
|
|
"rangeValueDelta": adjustment,
|
|
|
|
"rangeValueDeltaDefault": delta_default,
|
|
|
|
}
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
namespace, name, endpoint, service, hass, payload=payload, instance=instance
|
|
|
|
)
|
|
|
|
assert call.data[changed_parameter] == result_range
|
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_temp_sensor(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test temperature sensor discovery."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"sensor.test_temp",
|
|
|
|
"42",
|
|
|
|
{"friendly_name": "Test Temp Sensor", "unit_of_measurement": TEMP_FAHRENHEIT},
|
2018-01-30 04:33:39 +00:00
|
|
|
)
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "sensor#test_temp"
|
|
|
|
assert appliance["displayCategories"][0] == "TEMPERATURE_SENSOR"
|
|
|
|
assert appliance["friendlyName"] == "Test Temp Sensor"
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-01-10 23:52:21 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.TemperatureSensor", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
temp_sensor_capability = get_capability(capabilities, "Alexa.TemperatureSensor")
|
2019-01-10 23:52:21 +00:00
|
|
|
assert temp_sensor_capability is not None
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = temp_sensor_capability["properties"]
|
|
|
|
assert properties["retrievable"] is True
|
|
|
|
assert {"name": "temperature"} in properties["supported"]
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = await reported_properties(hass, "sensor#test_temp")
|
|
|
|
properties.assert_equal(
|
|
|
|
"Alexa.TemperatureSensor", "temperature", {"value": 42.0, "scale": "FAHRENHEIT"}
|
|
|
|
)
|
2018-01-29 00:43:27 +00:00
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_contact_sensor(hass):
|
2018-10-25 14:46:43 +00:00
|
|
|
"""Test contact sensor discovery."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"binary_sensor.test_contact",
|
|
|
|
"on",
|
|
|
|
{"friendly_name": "Test Contact Sensor", "device_class": "door"},
|
2018-10-25 14:46:43 +00:00
|
|
|
)
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-10-25 14:46:43 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "binary_sensor#test_contact"
|
|
|
|
assert appliance["displayCategories"][0] == "CONTACT_SENSOR"
|
|
|
|
assert appliance["friendlyName"] == "Test Contact Sensor"
|
2018-10-25 14:46:43 +00:00
|
|
|
|
2019-01-10 23:52:21 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.ContactSensor", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
contact_sensor_capability = get_capability(capabilities, "Alexa.ContactSensor")
|
2019-01-10 23:52:21 +00:00
|
|
|
assert contact_sensor_capability is not None
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = contact_sensor_capability["properties"]
|
|
|
|
assert properties["retrievable"] is True
|
|
|
|
assert {"name": "detectionState"} in properties["supported"]
|
2018-10-25 14:46:43 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = await reported_properties(hass, "binary_sensor#test_contact")
|
|
|
|
properties.assert_equal("Alexa.ContactSensor", "detectionState", "DETECTED")
|
2018-10-25 14:46:43 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
properties.assert_equal("Alexa.EndpointHealth", "connectivity", {"value": "OK"})
|
2019-01-10 23:52:21 +00:00
|
|
|
|
2018-10-25 14:46:43 +00:00
|
|
|
|
2019-12-03 00:02:17 +00:00
|
|
|
async def test_forced_contact_sensor(hass):
|
|
|
|
"""Test contact sensor discovery with specified display_category."""
|
|
|
|
device = (
|
|
|
|
"binary_sensor.test_contact_forced",
|
|
|
|
"on",
|
|
|
|
{"friendly_name": "Test Contact Sensor With DisplayCategory"},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "binary_sensor#test_contact_forced"
|
|
|
|
assert appliance["displayCategories"][0] == "CONTACT_SENSOR"
|
|
|
|
assert appliance["friendlyName"] == "Test Contact Sensor With DisplayCategory"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa.ContactSensor", "Alexa.EndpointHealth", "Alexa"
|
|
|
|
)
|
|
|
|
|
|
|
|
contact_sensor_capability = get_capability(capabilities, "Alexa.ContactSensor")
|
|
|
|
assert contact_sensor_capability is not None
|
|
|
|
properties = contact_sensor_capability["properties"]
|
|
|
|
assert properties["retrievable"] is True
|
|
|
|
assert {"name": "detectionState"} in properties["supported"]
|
|
|
|
|
|
|
|
properties = await reported_properties(hass, "binary_sensor#test_contact_forced")
|
|
|
|
properties.assert_equal("Alexa.ContactSensor", "detectionState", "DETECTED")
|
|
|
|
|
|
|
|
properties.assert_equal("Alexa.EndpointHealth", "connectivity", {"value": "OK"})
|
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_motion_sensor(hass):
|
2018-10-26 21:43:31 +00:00
|
|
|
"""Test motion sensor discovery."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"binary_sensor.test_motion",
|
|
|
|
"on",
|
|
|
|
{"friendly_name": "Test Motion Sensor", "device_class": "motion"},
|
2018-10-26 21:43:31 +00:00
|
|
|
)
|
2018-10-29 21:57:27 +00:00
|
|
|
appliance = await discovery_test(device, hass)
|
2018-10-26 21:43:31 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "binary_sensor#test_motion"
|
|
|
|
assert appliance["displayCategories"][0] == "MOTION_SENSOR"
|
|
|
|
assert appliance["friendlyName"] == "Test Motion Sensor"
|
2018-10-26 21:43:31 +00:00
|
|
|
|
2019-01-10 23:52:21 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.MotionSensor", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
motion_sensor_capability = get_capability(capabilities, "Alexa.MotionSensor")
|
2019-01-10 23:52:21 +00:00
|
|
|
assert motion_sensor_capability is not None
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = motion_sensor_capability["properties"]
|
|
|
|
assert properties["retrievable"] is True
|
|
|
|
assert {"name": "detectionState"} in properties["supported"]
|
2018-10-26 21:43:31 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = await reported_properties(hass, "binary_sensor#test_motion")
|
|
|
|
properties.assert_equal("Alexa.MotionSensor", "detectionState", "DETECTED")
|
2018-10-26 21:43:31 +00:00
|
|
|
|
|
|
|
|
2019-12-03 00:02:17 +00:00
|
|
|
async def test_forced_motion_sensor(hass):
|
|
|
|
"""Test motion sensor discovery with specified display_category."""
|
|
|
|
device = (
|
|
|
|
"binary_sensor.test_motion_forced",
|
|
|
|
"on",
|
|
|
|
{"friendly_name": "Test Motion Sensor With DisplayCategory"},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "binary_sensor#test_motion_forced"
|
|
|
|
assert appliance["displayCategories"][0] == "MOTION_SENSOR"
|
|
|
|
assert appliance["friendlyName"] == "Test Motion Sensor With DisplayCategory"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa.MotionSensor", "Alexa.EndpointHealth", "Alexa"
|
|
|
|
)
|
|
|
|
|
|
|
|
motion_sensor_capability = get_capability(capabilities, "Alexa.MotionSensor")
|
|
|
|
assert motion_sensor_capability is not None
|
|
|
|
properties = motion_sensor_capability["properties"]
|
|
|
|
assert properties["retrievable"] is True
|
|
|
|
assert {"name": "detectionState"} in properties["supported"]
|
|
|
|
|
|
|
|
properties = await reported_properties(hass, "binary_sensor#test_motion_forced")
|
|
|
|
properties.assert_equal("Alexa.MotionSensor", "detectionState", "DETECTED")
|
|
|
|
|
|
|
|
properties.assert_equal("Alexa.EndpointHealth", "connectivity", {"value": "OK"})
|
|
|
|
|
|
|
|
|
2019-10-23 18:41:26 +00:00
|
|
|
async def test_doorbell_sensor(hass):
|
|
|
|
"""Test doorbell sensor discovery."""
|
|
|
|
device = (
|
|
|
|
"binary_sensor.test_doorbell",
|
|
|
|
"off",
|
|
|
|
{"friendly_name": "Test Doorbell Sensor", "device_class": "occupancy"},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "binary_sensor#test_doorbell"
|
|
|
|
assert appliance["displayCategories"][0] == "DOORBELL"
|
|
|
|
assert appliance["friendlyName"] == "Test Doorbell Sensor"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.DoorbellEventSource", "Alexa.EndpointHealth", "Alexa"
|
2019-10-23 18:41:26 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
doorbell_capability = get_capability(capabilities, "Alexa.DoorbellEventSource")
|
|
|
|
assert doorbell_capability is not None
|
|
|
|
assert doorbell_capability["proactivelyReported"] is True
|
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_unknown_sensor(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test sensors of unknown quantities are not discovered."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"sensor.test_sickness",
|
|
|
|
"0.1",
|
|
|
|
{"friendly_name": "Test Space Sickness Sensor", "unit_of_measurement": "garn"},
|
|
|
|
)
|
2018-10-29 21:57:27 +00:00
|
|
|
await discovery_test(device, hass, expected_endpoints=0)
|
2017-09-16 19:35:28 +00:00
|
|
|
|
|
|
|
|
2018-03-30 06:49:08 +00:00
|
|
|
async def test_thermostat(hass):
|
|
|
|
"""Test thermostat discovery."""
|
2018-08-22 07:17:29 +00:00
|
|
|
hass.config.units.temperature_unit = TEMP_FAHRENHEIT
|
2018-03-30 06:49:08 +00:00
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"climate.test_thermostat",
|
|
|
|
"cool",
|
2018-03-30 06:49:08 +00:00
|
|
|
{
|
2019-07-31 19:25:30 +00:00
|
|
|
"temperature": 70.0,
|
|
|
|
"target_temp_high": 80.0,
|
|
|
|
"target_temp_low": 60.0,
|
|
|
|
"current_temperature": 75.0,
|
|
|
|
"friendly_name": "Test Thermostat",
|
|
|
|
"supported_features": 1 | 2 | 4 | 128,
|
2019-10-25 20:42:21 +00:00
|
|
|
"hvac_modes": ["off", "heat", "cool", "auto", "dry"],
|
2019-07-31 19:25:30 +00:00
|
|
|
"preset_mode": None,
|
|
|
|
"preset_modes": ["eco"],
|
|
|
|
"min_temp": 50,
|
|
|
|
"max_temp": 90,
|
|
|
|
},
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert appliance["endpointId"] == "climate#test_thermostat"
|
|
|
|
assert appliance["displayCategories"][0] == "THERMOSTAT"
|
|
|
|
assert appliance["friendlyName"] == "Test Thermostat"
|
2018-03-30 06:49:08 +00:00
|
|
|
|
2019-10-25 20:42:21 +00:00
|
|
|
capabilities = assert_endpoint_capabilities(
|
2018-03-30 06:49:08 +00:00
|
|
|
appliance,
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.PowerController",
|
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"Alexa.TemperatureSensor",
|
|
|
|
"Alexa.EndpointHealth",
|
2019-11-26 07:05:10 +00:00
|
|
|
"Alexa",
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = await reported_properties(hass, "climate#test_thermostat")
|
|
|
|
properties.assert_equal("Alexa.ThermostatController", "thermostatMode", "COOL")
|
2018-03-30 06:49:08 +00:00
|
|
|
properties.assert_equal(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"targetSetpoint",
|
|
|
|
{"value": 70.0, "scale": "FAHRENHEIT"},
|
|
|
|
)
|
2018-03-30 06:49:08 +00:00
|
|
|
properties.assert_equal(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.TemperatureSensor", "temperature", {"value": 75.0, "scale": "FAHRENHEIT"}
|
|
|
|
)
|
2018-03-30 06:49:08 +00:00
|
|
|
|
2019-10-25 20:42:21 +00:00
|
|
|
thermostat_capability = get_capability(capabilities, "Alexa.ThermostatController")
|
|
|
|
assert thermostat_capability is not None
|
|
|
|
configuration = thermostat_capability["configuration"]
|
|
|
|
assert configuration["supportsScheduling"] is False
|
|
|
|
|
|
|
|
supported_modes = ["OFF", "HEAT", "COOL", "AUTO", "ECO", "CUSTOM"]
|
|
|
|
for mode in supported_modes:
|
|
|
|
assert mode in configuration["supportedModes"]
|
|
|
|
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
call, msg = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetTargetTemperature",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_temperature",
|
2018-03-30 06:49:08 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"targetSetpoint": {"value": 69.0, "scale": "FAHRENHEIT"}},
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert call.data["temperature"] == 69.0
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
properties.assert_equal(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"targetSetpoint",
|
|
|
|
{"value": 69.0, "scale": "FAHRENHEIT"},
|
|
|
|
)
|
2018-03-30 06:49:08 +00:00
|
|
|
|
|
|
|
msg = await assert_request_fails(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetTargetTemperature",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_temperature",
|
2018-03-30 06:49:08 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"targetSetpoint": {"value": 0.0, "scale": "CELSIUS"}},
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["event"]["payload"]["type"] == "TEMPERATURE_VALUE_OUT_OF_RANGE"
|
2018-03-30 06:49:08 +00:00
|
|
|
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
call, msg = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetTargetTemperature",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_temperature",
|
2018-03-30 06:49:08 +00:00
|
|
|
hass,
|
|
|
|
payload={
|
2019-07-31 19:25:30 +00:00
|
|
|
"targetSetpoint": {"value": 70.0, "scale": "FAHRENHEIT"},
|
|
|
|
"lowerSetpoint": {"value": 293.15, "scale": "KELVIN"},
|
|
|
|
"upperSetpoint": {"value": 30.0, "scale": "CELSIUS"},
|
|
|
|
},
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert call.data["temperature"] == 70.0
|
|
|
|
assert call.data["target_temp_low"] == 68.0
|
|
|
|
assert call.data["target_temp_high"] == 86.0
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
properties.assert_equal(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"targetSetpoint",
|
|
|
|
{"value": 70.0, "scale": "FAHRENHEIT"},
|
|
|
|
)
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
properties.assert_equal(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"lowerSetpoint",
|
|
|
|
{"value": 68.0, "scale": "FAHRENHEIT"},
|
|
|
|
)
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
properties.assert_equal(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"upperSetpoint",
|
|
|
|
{"value": 86.0, "scale": "FAHRENHEIT"},
|
|
|
|
)
|
2018-03-30 06:49:08 +00:00
|
|
|
|
|
|
|
msg = await assert_request_fails(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetTargetTemperature",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_temperature",
|
2018-03-30 06:49:08 +00:00
|
|
|
hass,
|
|
|
|
payload={
|
2019-07-31 19:25:30 +00:00
|
|
|
"lowerSetpoint": {"value": 273.15, "scale": "KELVIN"},
|
|
|
|
"upperSetpoint": {"value": 75.0, "scale": "FAHRENHEIT"},
|
|
|
|
},
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["event"]["payload"]["type"] == "TEMPERATURE_VALUE_OUT_OF_RANGE"
|
2018-03-30 06:49:08 +00:00
|
|
|
|
|
|
|
msg = await assert_request_fails(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetTargetTemperature",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_temperature",
|
2018-03-30 06:49:08 +00:00
|
|
|
hass,
|
|
|
|
payload={
|
2019-07-31 19:25:30 +00:00
|
|
|
"lowerSetpoint": {"value": 293.15, "scale": "FAHRENHEIT"},
|
|
|
|
"upperSetpoint": {"value": 75.0, "scale": "CELSIUS"},
|
|
|
|
},
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["event"]["payload"]["type"] == "TEMPERATURE_VALUE_OUT_OF_RANGE"
|
2018-03-30 06:49:08 +00:00
|
|
|
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
call, msg = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"AdjustTargetTemperature",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_temperature",
|
2018-03-30 06:49:08 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"targetSetpointDelta": {"value": -10.0, "scale": "KELVIN"}},
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert call.data["temperature"] == 52.0
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
properties.assert_equal(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"targetSetpoint",
|
|
|
|
{"value": 52.0, "scale": "FAHRENHEIT"},
|
|
|
|
)
|
2018-03-30 06:49:08 +00:00
|
|
|
|
|
|
|
msg = await assert_request_fails(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"AdjustTargetTemperature",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_temperature",
|
2018-03-30 06:49:08 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"targetSetpointDelta": {"value": 20.0, "scale": "CELSIUS"}},
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["event"]["payload"]["type"] == "TEMPERATURE_VALUE_OUT_OF_RANGE"
|
2018-03-30 06:49:08 +00:00
|
|
|
|
2018-10-31 15:09:13 +00:00
|
|
|
# Setting mode, the payload can be an object with a value attribute...
|
|
|
|
call, msg = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetThermostatMode",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_hvac_mode",
|
2018-03-30 06:49:08 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"thermostatMode": {"value": "HEAT"}},
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert call.data["hvac_mode"] == "heat"
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
|
|
|
properties.assert_equal("Alexa.ThermostatController", "thermostatMode", "HEAT")
|
2018-03-30 06:49:08 +00:00
|
|
|
|
2018-10-31 15:09:13 +00:00
|
|
|
call, msg = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetThermostatMode",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_hvac_mode",
|
2018-04-18 18:19:05 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"thermostatMode": {"value": "COOL"}},
|
2018-04-18 18:19:05 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert call.data["hvac_mode"] == "cool"
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
|
|
|
properties.assert_equal("Alexa.ThermostatController", "thermostatMode", "COOL")
|
2018-04-18 18:19:05 +00:00
|
|
|
|
2018-10-31 15:09:13 +00:00
|
|
|
# ...it can also be just the mode.
|
|
|
|
call, msg = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetThermostatMode",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_hvac_mode",
|
2018-10-31 15:09:13 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"thermostatMode": "HEAT"},
|
2018-10-31 15:09:13 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert call.data["hvac_mode"] == "heat"
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
|
|
|
properties.assert_equal("Alexa.ThermostatController", "thermostatMode", "HEAT")
|
2018-10-31 15:09:13 +00:00
|
|
|
|
2019-10-25 20:42:21 +00:00
|
|
|
# Assert we can call custom modes
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetThermostatMode",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_hvac_mode",
|
|
|
|
hass,
|
|
|
|
payload={"thermostatMode": {"value": "CUSTOM", "customName": "DEHUMIDIFY"}},
|
|
|
|
)
|
|
|
|
assert call.data["hvac_mode"] == "dry"
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
|
|
|
properties.assert_equal("Alexa.ThermostatController", "thermostatMode", "CUSTOM")
|
|
|
|
|
|
|
|
# assert unsupported custom mode
|
|
|
|
msg = await assert_request_fails(
|
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetThermostatMode",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_hvac_mode",
|
|
|
|
hass,
|
|
|
|
payload={"thermostatMode": {"value": "CUSTOM", "customName": "INVALID"}},
|
|
|
|
)
|
|
|
|
assert msg["event"]["payload"]["type"] == "UNSUPPORTED_THERMOSTAT_MODE"
|
|
|
|
|
2018-03-30 06:49:08 +00:00
|
|
|
msg = await assert_request_fails(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetThermostatMode",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_hvac_mode",
|
2018-03-30 06:49:08 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"thermostatMode": {"value": "INVALID"}},
|
2018-03-30 06:49:08 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["event"]["payload"]["type"] == "UNSUPPORTED_THERMOSTAT_MODE"
|
2018-03-30 06:49:08 +00:00
|
|
|
|
2018-10-29 19:52:34 +00:00
|
|
|
call, _ = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetThermostatMode",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_hvac_mode",
|
2018-10-29 19:52:34 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"thermostatMode": "OFF"},
|
2018-10-29 19:52:34 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert call.data["hvac_mode"] == "off"
|
Climate 1.0 (#23899)
* Climate 1.0 / part 1/2/3
* fix flake
* Lint
* Update Google Assistant
* ambiclimate to climate 1.0 (#24911)
* Fix Alexa
* Lint
* Migrate zhong_hong
* Migrate tuya
* Migrate honeywell to new climate schema (#24257)
* Update one
* Fix model climate v2
* Cleanup p4
* Add comfort hold mode
* Fix old code
* Update homeassistant/components/climate/__init__.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* Update homeassistant/components/climate/const.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* First renaming
* Rename operation to hvac for paulus
* Rename hold mode to preset mode
* Cleanup & update comments
* Remove on/off
* Fix supported feature count
* Update services
* Update demo
* Fix tests & use current_hvac
* Update comment
* Fix tests & add typing
* Add more typing
* Update modes
* Fix tests
* Cleanup low/high with range
* Update homematic part 1
* Finish homematic
* Fix lint
* fix hm mapping
* Support simple devices
* convert lcn
* migrate oem
* Fix xs1
* update hive
* update mil
* Update toon
* migrate deconz
* cleanup
* update tesla
* Fix lint
* Fix vera
* Migrate zwave
* Migrate velbus
* Cleanup humity feature
* Cleanup
* Migrate wink
* migrate dyson
* Fix current hvac
* Renaming
* Fix lint
* Migrate tfiac
* migrate tado
* Fix PRESET can be None
* apply PR#23913 from dev
* remove EU component, etc.
* remove EU component, etc.
* ready to test now
* de-linted
* some tweaks
* de-lint
* better handling of edge cases
* delint
* fix set_mode typos
* apply PR#23913 from dev
* remove EU component, etc.
* ready to test now
* de-linted
* some tweaks
* de-lint
* better handling of edge cases
* delint
* fix set_mode typos
* delint, move debug code
* away preset now working
* code tidy-up
* code tidy-up 2
* code tidy-up 3
* address issues #18932, #15063
* address issues #18932, #15063 - 2/2
* refactor MODE_AUTO to MODE_HEAT_COOL and use F not C
* add low/high to set_temp
* add low/high to set_temp 2
* add low/high to set_temp - delint
* run HA scripts
* port changes from PR #24402
* manual rebase
* manual rebase 2
* delint
* minor change
* remove SUPPORT_HVAC_ACTION
* Migrate radiotherm
* Convert touchline
* Migrate flexit
* Migrate nuheat
* Migrate maxcube
* Fix names maxcube const
* Migrate proliphix
* Migrate heatmiser
* Migrate fritzbox
* Migrate opentherm_gw
* Migrate venstar
* Migrate daikin
* Migrate modbus
* Fix elif
* Migrate Homematic IP Cloud to climate-1.0 (#24913)
* hmip climate fix
* Update hvac_mode and preset_mode
* fix lint
* Fix lint
* Migrate generic_thermostat
* Migrate incomfort to new climate schema (#24915)
* initial commit
* Update climate.py
* Migrate eq3btsmart
* Lint
* cleanup PRESET_MANUAL
* Migrate ecobee
* No conditional features
* KNX: Migrate climate component to new climate platform (#24931)
* Migrate climate component
* Remove unused code
* Corrected line length
* Lint
* Lint
* fix tests
* Fix value
* Migrate geniushub to new climate schema (#24191)
* Update one
* Fix model climate v2
* Cleanup p4
* Add comfort hold mode
* Fix old code
* Update homeassistant/components/climate/__init__.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* Update homeassistant/components/climate/const.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* First renaming
* Rename operation to hvac for paulus
* Rename hold mode to preset mode
* Cleanup & update comments
* Remove on/off
* Fix supported feature count
* Update services
* Update demo
* Fix tests & use current_hvac
* Update comment
* Fix tests & add typing
* Add more typing
* Update modes
* Fix tests
* Cleanup low/high with range
* Update homematic part 1
* Finish homematic
* Fix lint
* fix hm mapping
* Support simple devices
* convert lcn
* migrate oem
* Fix xs1
* update hive
* update mil
* Update toon
* migrate deconz
* cleanup
* update tesla
* Fix lint
* Fix vera
* Migrate zwave
* Migrate velbus
* Cleanup humity feature
* Cleanup
* Migrate wink
* migrate dyson
* Fix current hvac
* Renaming
* Fix lint
* Migrate tfiac
* migrate tado
* delinted
* delinted
* use latest client
* clean up mappings
* clean up mappings
* add duration to set_temperature
* add duration to set_temperature
* manual rebase
* tweak
* fix regression
* small fix
* fix rebase mixup
* address comments
* finish refactor
* fix regression
* tweak type hints
* delint
* manual rebase
* WIP: Fixes for honeywell migration to climate-1.0 (#24938)
* add type hints
* code tidy-up
* Fixes for incomfort migration to climate-1.0 (#24936)
* delint type hints
* no async unless await
* revert: no async unless await
* revert: no async unless await 2
* delint
* fix typo
* Fix homekit_controller on climate-1.0 (#24948)
* Fix tests on climate-1.0 branch
* As part of climate-1.0, make state return the heating-cooling.current characteristic
* Fixes from review
* lint
* Fix imports
* Migrate stibel_eltron
* Fix lint
* Migrate coolmaster to climate 1.0 (#24967)
* Migrate coolmaster to climate 1.0
* fix lint errors
* More lint fixes
* Fix demo to work with UI
* Migrate spider
* Demo update
* Updated frontend to 20190705.0
* Fix boost mode (#24980)
* Prepare Netatmo for climate 1.0 (#24973)
* Migration Netatmo
* Address comments
* Update climate.py
* Migrate ephember
* Migrate Sensibo
* Implemented review comments (#24942)
* Migrate ESPHome
* Migrate MQTT
* Migrate Nest
* Migrate melissa
* Initial/partial migration of ST
* Migrate ST
* Remove Away mode (#24995)
* Migrate evohome, cache access tokens (#24491)
* add water_heater, add storage - initial commit
* add water_heater, add storage - initial commit
delint
add missing code
desiderata
update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
delint
* Add Broker, Water Heater & Refactor
add missing code
desiderata
* update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
* bugfix - loc_idx may not be 0
more refactor - ensure pure async
more refactoring
appears all r/o attributes are working
tweak precsion, DHW & delint
remove unused code
remove unused code 2
remove unused code, refactor _save_auth_tokens()
* support RoundThermostat
bugfix opmode, switch to util.dt, add until=1h
revert breaking change
* store at_expires as naive UTC
remove debug code
delint
tidy up exception handling
delint
add water_heater, add storage - initial commit
delint
add missing code
desiderata
update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
add water_heater, add storage - initial commit
delint
add missing code
desiderata
update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
delint
bugfix - loc_idx may not be 0
more refactor - ensure pure async
more refactoring
appears all r/o attributes are working
tweak precsion, DHW & delint
remove unused code
remove unused code 2
remove unused code, refactor _save_auth_tokens()
support RoundThermostat
bugfix opmode, switch to util.dt, add until=1h
revert breaking change
store at_expires as naive UTC
remove debug code
delint
tidy up exception handling
delint
* update CODEOWNERS
* fix regression
* fix requirements
* migrate to climate-1.0
* tweaking
* de-lint
* TCS working? & delint
* tweaking
* TCS code finalised
* remove available() logic
* refactor _switchpoints()
* tidy up switchpoint code
* tweak
* teaking device_state_attributes
* some refactoring
* move PRESET_CUSTOM back to evohome
* move CONF_ACCESS_TOKEN_EXPIRES CONF_REFRESH_TOKEN back to evohome
* refactor SP code and dt conversion
* delinted
* delinted
* remove water_heater
* fix regression
* Migrate homekit
* Cleanup away mode
* Fix tests
* add helpers
* fix tests melissa
* Fix nehueat
* fix zwave
* add more tests
* fix deconz
* Fix climate test emulate_hue
* fix tests
* fix dyson tests
* fix demo with new layout
* fix honeywell
* Switch homekit_controller to use HVAC_MODE_HEAT_COOL instead of HVAC_MODE_AUTO (#25009)
* Lint
* PyLint
* Pylint
* fix fritzbox tests
* Fix google
* Fix all tests
* Fix lint
* Fix auto for homekit like controler
* Fix lint
* fix lint
2019-07-08 12:00:24 +00:00
|
|
|
|
|
|
|
# Assert we can call presets
|
|
|
|
call, msg = await assert_request_calls_service(
|
2019-07-31 19:25:30 +00:00
|
|
|
"Alexa.ThermostatController",
|
|
|
|
"SetThermostatMode",
|
|
|
|
"climate#test_thermostat",
|
|
|
|
"climate.set_preset_mode",
|
Climate 1.0 (#23899)
* Climate 1.0 / part 1/2/3
* fix flake
* Lint
* Update Google Assistant
* ambiclimate to climate 1.0 (#24911)
* Fix Alexa
* Lint
* Migrate zhong_hong
* Migrate tuya
* Migrate honeywell to new climate schema (#24257)
* Update one
* Fix model climate v2
* Cleanup p4
* Add comfort hold mode
* Fix old code
* Update homeassistant/components/climate/__init__.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* Update homeassistant/components/climate/const.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* First renaming
* Rename operation to hvac for paulus
* Rename hold mode to preset mode
* Cleanup & update comments
* Remove on/off
* Fix supported feature count
* Update services
* Update demo
* Fix tests & use current_hvac
* Update comment
* Fix tests & add typing
* Add more typing
* Update modes
* Fix tests
* Cleanup low/high with range
* Update homematic part 1
* Finish homematic
* Fix lint
* fix hm mapping
* Support simple devices
* convert lcn
* migrate oem
* Fix xs1
* update hive
* update mil
* Update toon
* migrate deconz
* cleanup
* update tesla
* Fix lint
* Fix vera
* Migrate zwave
* Migrate velbus
* Cleanup humity feature
* Cleanup
* Migrate wink
* migrate dyson
* Fix current hvac
* Renaming
* Fix lint
* Migrate tfiac
* migrate tado
* Fix PRESET can be None
* apply PR#23913 from dev
* remove EU component, etc.
* remove EU component, etc.
* ready to test now
* de-linted
* some tweaks
* de-lint
* better handling of edge cases
* delint
* fix set_mode typos
* apply PR#23913 from dev
* remove EU component, etc.
* ready to test now
* de-linted
* some tweaks
* de-lint
* better handling of edge cases
* delint
* fix set_mode typos
* delint, move debug code
* away preset now working
* code tidy-up
* code tidy-up 2
* code tidy-up 3
* address issues #18932, #15063
* address issues #18932, #15063 - 2/2
* refactor MODE_AUTO to MODE_HEAT_COOL and use F not C
* add low/high to set_temp
* add low/high to set_temp 2
* add low/high to set_temp - delint
* run HA scripts
* port changes from PR #24402
* manual rebase
* manual rebase 2
* delint
* minor change
* remove SUPPORT_HVAC_ACTION
* Migrate radiotherm
* Convert touchline
* Migrate flexit
* Migrate nuheat
* Migrate maxcube
* Fix names maxcube const
* Migrate proliphix
* Migrate heatmiser
* Migrate fritzbox
* Migrate opentherm_gw
* Migrate venstar
* Migrate daikin
* Migrate modbus
* Fix elif
* Migrate Homematic IP Cloud to climate-1.0 (#24913)
* hmip climate fix
* Update hvac_mode and preset_mode
* fix lint
* Fix lint
* Migrate generic_thermostat
* Migrate incomfort to new climate schema (#24915)
* initial commit
* Update climate.py
* Migrate eq3btsmart
* Lint
* cleanup PRESET_MANUAL
* Migrate ecobee
* No conditional features
* KNX: Migrate climate component to new climate platform (#24931)
* Migrate climate component
* Remove unused code
* Corrected line length
* Lint
* Lint
* fix tests
* Fix value
* Migrate geniushub to new climate schema (#24191)
* Update one
* Fix model climate v2
* Cleanup p4
* Add comfort hold mode
* Fix old code
* Update homeassistant/components/climate/__init__.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* Update homeassistant/components/climate/const.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* First renaming
* Rename operation to hvac for paulus
* Rename hold mode to preset mode
* Cleanup & update comments
* Remove on/off
* Fix supported feature count
* Update services
* Update demo
* Fix tests & use current_hvac
* Update comment
* Fix tests & add typing
* Add more typing
* Update modes
* Fix tests
* Cleanup low/high with range
* Update homematic part 1
* Finish homematic
* Fix lint
* fix hm mapping
* Support simple devices
* convert lcn
* migrate oem
* Fix xs1
* update hive
* update mil
* Update toon
* migrate deconz
* cleanup
* update tesla
* Fix lint
* Fix vera
* Migrate zwave
* Migrate velbus
* Cleanup humity feature
* Cleanup
* Migrate wink
* migrate dyson
* Fix current hvac
* Renaming
* Fix lint
* Migrate tfiac
* migrate tado
* delinted
* delinted
* use latest client
* clean up mappings
* clean up mappings
* add duration to set_temperature
* add duration to set_temperature
* manual rebase
* tweak
* fix regression
* small fix
* fix rebase mixup
* address comments
* finish refactor
* fix regression
* tweak type hints
* delint
* manual rebase
* WIP: Fixes for honeywell migration to climate-1.0 (#24938)
* add type hints
* code tidy-up
* Fixes for incomfort migration to climate-1.0 (#24936)
* delint type hints
* no async unless await
* revert: no async unless await
* revert: no async unless await 2
* delint
* fix typo
* Fix homekit_controller on climate-1.0 (#24948)
* Fix tests on climate-1.0 branch
* As part of climate-1.0, make state return the heating-cooling.current characteristic
* Fixes from review
* lint
* Fix imports
* Migrate stibel_eltron
* Fix lint
* Migrate coolmaster to climate 1.0 (#24967)
* Migrate coolmaster to climate 1.0
* fix lint errors
* More lint fixes
* Fix demo to work with UI
* Migrate spider
* Demo update
* Updated frontend to 20190705.0
* Fix boost mode (#24980)
* Prepare Netatmo for climate 1.0 (#24973)
* Migration Netatmo
* Address comments
* Update climate.py
* Migrate ephember
* Migrate Sensibo
* Implemented review comments (#24942)
* Migrate ESPHome
* Migrate MQTT
* Migrate Nest
* Migrate melissa
* Initial/partial migration of ST
* Migrate ST
* Remove Away mode (#24995)
* Migrate evohome, cache access tokens (#24491)
* add water_heater, add storage - initial commit
* add water_heater, add storage - initial commit
delint
add missing code
desiderata
update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
delint
* Add Broker, Water Heater & Refactor
add missing code
desiderata
* update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
* bugfix - loc_idx may not be 0
more refactor - ensure pure async
more refactoring
appears all r/o attributes are working
tweak precsion, DHW & delint
remove unused code
remove unused code 2
remove unused code, refactor _save_auth_tokens()
* support RoundThermostat
bugfix opmode, switch to util.dt, add until=1h
revert breaking change
* store at_expires as naive UTC
remove debug code
delint
tidy up exception handling
delint
add water_heater, add storage - initial commit
delint
add missing code
desiderata
update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
add water_heater, add storage - initial commit
delint
add missing code
desiderata
update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
delint
bugfix - loc_idx may not be 0
more refactor - ensure pure async
more refactoring
appears all r/o attributes are working
tweak precsion, DHW & delint
remove unused code
remove unused code 2
remove unused code, refactor _save_auth_tokens()
support RoundThermostat
bugfix opmode, switch to util.dt, add until=1h
revert breaking change
store at_expires as naive UTC
remove debug code
delint
tidy up exception handling
delint
* update CODEOWNERS
* fix regression
* fix requirements
* migrate to climate-1.0
* tweaking
* de-lint
* TCS working? & delint
* tweaking
* TCS code finalised
* remove available() logic
* refactor _switchpoints()
* tidy up switchpoint code
* tweak
* teaking device_state_attributes
* some refactoring
* move PRESET_CUSTOM back to evohome
* move CONF_ACCESS_TOKEN_EXPIRES CONF_REFRESH_TOKEN back to evohome
* refactor SP code and dt conversion
* delinted
* delinted
* remove water_heater
* fix regression
* Migrate homekit
* Cleanup away mode
* Fix tests
* add helpers
* fix tests melissa
* Fix nehueat
* fix zwave
* add more tests
* fix deconz
* Fix climate test emulate_hue
* fix tests
* fix dyson tests
* fix demo with new layout
* fix honeywell
* Switch homekit_controller to use HVAC_MODE_HEAT_COOL instead of HVAC_MODE_AUTO (#25009)
* Lint
* PyLint
* Pylint
* fix fritzbox tests
* Fix google
* Fix all tests
* Fix lint
* Fix auto for homekit like controler
* Fix lint
* fix lint
2019-07-08 12:00:24 +00:00
|
|
|
hass,
|
2019-07-31 19:25:30 +00:00
|
|
|
payload={"thermostatMode": "ECO"},
|
Climate 1.0 (#23899)
* Climate 1.0 / part 1/2/3
* fix flake
* Lint
* Update Google Assistant
* ambiclimate to climate 1.0 (#24911)
* Fix Alexa
* Lint
* Migrate zhong_hong
* Migrate tuya
* Migrate honeywell to new climate schema (#24257)
* Update one
* Fix model climate v2
* Cleanup p4
* Add comfort hold mode
* Fix old code
* Update homeassistant/components/climate/__init__.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* Update homeassistant/components/climate/const.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* First renaming
* Rename operation to hvac for paulus
* Rename hold mode to preset mode
* Cleanup & update comments
* Remove on/off
* Fix supported feature count
* Update services
* Update demo
* Fix tests & use current_hvac
* Update comment
* Fix tests & add typing
* Add more typing
* Update modes
* Fix tests
* Cleanup low/high with range
* Update homematic part 1
* Finish homematic
* Fix lint
* fix hm mapping
* Support simple devices
* convert lcn
* migrate oem
* Fix xs1
* update hive
* update mil
* Update toon
* migrate deconz
* cleanup
* update tesla
* Fix lint
* Fix vera
* Migrate zwave
* Migrate velbus
* Cleanup humity feature
* Cleanup
* Migrate wink
* migrate dyson
* Fix current hvac
* Renaming
* Fix lint
* Migrate tfiac
* migrate tado
* Fix PRESET can be None
* apply PR#23913 from dev
* remove EU component, etc.
* remove EU component, etc.
* ready to test now
* de-linted
* some tweaks
* de-lint
* better handling of edge cases
* delint
* fix set_mode typos
* apply PR#23913 from dev
* remove EU component, etc.
* ready to test now
* de-linted
* some tweaks
* de-lint
* better handling of edge cases
* delint
* fix set_mode typos
* delint, move debug code
* away preset now working
* code tidy-up
* code tidy-up 2
* code tidy-up 3
* address issues #18932, #15063
* address issues #18932, #15063 - 2/2
* refactor MODE_AUTO to MODE_HEAT_COOL and use F not C
* add low/high to set_temp
* add low/high to set_temp 2
* add low/high to set_temp - delint
* run HA scripts
* port changes from PR #24402
* manual rebase
* manual rebase 2
* delint
* minor change
* remove SUPPORT_HVAC_ACTION
* Migrate radiotherm
* Convert touchline
* Migrate flexit
* Migrate nuheat
* Migrate maxcube
* Fix names maxcube const
* Migrate proliphix
* Migrate heatmiser
* Migrate fritzbox
* Migrate opentherm_gw
* Migrate venstar
* Migrate daikin
* Migrate modbus
* Fix elif
* Migrate Homematic IP Cloud to climate-1.0 (#24913)
* hmip climate fix
* Update hvac_mode and preset_mode
* fix lint
* Fix lint
* Migrate generic_thermostat
* Migrate incomfort to new climate schema (#24915)
* initial commit
* Update climate.py
* Migrate eq3btsmart
* Lint
* cleanup PRESET_MANUAL
* Migrate ecobee
* No conditional features
* KNX: Migrate climate component to new climate platform (#24931)
* Migrate climate component
* Remove unused code
* Corrected line length
* Lint
* Lint
* fix tests
* Fix value
* Migrate geniushub to new climate schema (#24191)
* Update one
* Fix model climate v2
* Cleanup p4
* Add comfort hold mode
* Fix old code
* Update homeassistant/components/climate/__init__.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* Update homeassistant/components/climate/const.py
Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>
* First renaming
* Rename operation to hvac for paulus
* Rename hold mode to preset mode
* Cleanup & update comments
* Remove on/off
* Fix supported feature count
* Update services
* Update demo
* Fix tests & use current_hvac
* Update comment
* Fix tests & add typing
* Add more typing
* Update modes
* Fix tests
* Cleanup low/high with range
* Update homematic part 1
* Finish homematic
* Fix lint
* fix hm mapping
* Support simple devices
* convert lcn
* migrate oem
* Fix xs1
* update hive
* update mil
* Update toon
* migrate deconz
* cleanup
* update tesla
* Fix lint
* Fix vera
* Migrate zwave
* Migrate velbus
* Cleanup humity feature
* Cleanup
* Migrate wink
* migrate dyson
* Fix current hvac
* Renaming
* Fix lint
* Migrate tfiac
* migrate tado
* delinted
* delinted
* use latest client
* clean up mappings
* clean up mappings
* add duration to set_temperature
* add duration to set_temperature
* manual rebase
* tweak
* fix regression
* small fix
* fix rebase mixup
* address comments
* finish refactor
* fix regression
* tweak type hints
* delint
* manual rebase
* WIP: Fixes for honeywell migration to climate-1.0 (#24938)
* add type hints
* code tidy-up
* Fixes for incomfort migration to climate-1.0 (#24936)
* delint type hints
* no async unless await
* revert: no async unless await
* revert: no async unless await 2
* delint
* fix typo
* Fix homekit_controller on climate-1.0 (#24948)
* Fix tests on climate-1.0 branch
* As part of climate-1.0, make state return the heating-cooling.current characteristic
* Fixes from review
* lint
* Fix imports
* Migrate stibel_eltron
* Fix lint
* Migrate coolmaster to climate 1.0 (#24967)
* Migrate coolmaster to climate 1.0
* fix lint errors
* More lint fixes
* Fix demo to work with UI
* Migrate spider
* Demo update
* Updated frontend to 20190705.0
* Fix boost mode (#24980)
* Prepare Netatmo for climate 1.0 (#24973)
* Migration Netatmo
* Address comments
* Update climate.py
* Migrate ephember
* Migrate Sensibo
* Implemented review comments (#24942)
* Migrate ESPHome
* Migrate MQTT
* Migrate Nest
* Migrate melissa
* Initial/partial migration of ST
* Migrate ST
* Remove Away mode (#24995)
* Migrate evohome, cache access tokens (#24491)
* add water_heater, add storage - initial commit
* add water_heater, add storage - initial commit
delint
add missing code
desiderata
update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
delint
* Add Broker, Water Heater & Refactor
add missing code
desiderata
* update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
* bugfix - loc_idx may not be 0
more refactor - ensure pure async
more refactoring
appears all r/o attributes are working
tweak precsion, DHW & delint
remove unused code
remove unused code 2
remove unused code, refactor _save_auth_tokens()
* support RoundThermostat
bugfix opmode, switch to util.dt, add until=1h
revert breaking change
* store at_expires as naive UTC
remove debug code
delint
tidy up exception handling
delint
add water_heater, add storage - initial commit
delint
add missing code
desiderata
update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
add water_heater, add storage - initial commit
delint
add missing code
desiderata
update honeywell client library & CODEOWNER
add auth_tokens code, refactor & delint
refactor for broker
delint
bugfix - loc_idx may not be 0
more refactor - ensure pure async
more refactoring
appears all r/o attributes are working
tweak precsion, DHW & delint
remove unused code
remove unused code 2
remove unused code, refactor _save_auth_tokens()
support RoundThermostat
bugfix opmode, switch to util.dt, add until=1h
revert breaking change
store at_expires as naive UTC
remove debug code
delint
tidy up exception handling
delint
* update CODEOWNERS
* fix regression
* fix requirements
* migrate to climate-1.0
* tweaking
* de-lint
* TCS working? & delint
* tweaking
* TCS code finalised
* remove available() logic
* refactor _switchpoints()
* tidy up switchpoint code
* tweak
* teaking device_state_attributes
* some refactoring
* move PRESET_CUSTOM back to evohome
* move CONF_ACCESS_TOKEN_EXPIRES CONF_REFRESH_TOKEN back to evohome
* refactor SP code and dt conversion
* delinted
* delinted
* remove water_heater
* fix regression
* Migrate homekit
* Cleanup away mode
* Fix tests
* add helpers
* fix tests melissa
* Fix nehueat
* fix zwave
* add more tests
* fix deconz
* Fix climate test emulate_hue
* fix tests
* fix dyson tests
* fix demo with new layout
* fix honeywell
* Switch homekit_controller to use HVAC_MODE_HEAT_COOL instead of HVAC_MODE_AUTO (#25009)
* Lint
* PyLint
* Pylint
* fix fritzbox tests
* Fix google
* Fix all tests
* Fix lint
* Fix auto for homekit like controler
* Fix lint
* fix lint
2019-07-08 12:00:24 +00:00
|
|
|
)
|
2019-07-31 19:25:30 +00:00
|
|
|
assert call.data["preset_mode"] == "eco"
|
2018-10-29 19:52:34 +00:00
|
|
|
|
2019-10-25 20:42:21 +00:00
|
|
|
# Reset config temperature_unit back to CELSIUS, required for additional tests outside this component.
|
|
|
|
hass.config.units.temperature_unit = TEMP_CELSIUS
|
|
|
|
|
2018-03-30 06:49:08 +00:00
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_exclude_filters(hass):
|
2017-11-18 05:10:24 +00:00
|
|
|
"""Test exclusion filters."""
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.Discovery", "Discover")
|
2017-11-18 05:10:24 +00:00
|
|
|
|
|
|
|
# setup test devices
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.states.async_set("switch.test", "on", {"friendly_name": "Test switch"})
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.states.async_set("script.deny", "off", {"friendly_name": "Blocked script"})
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.states.async_set("cover.deny", "off", {"friendly_name": "Blocked cover"})
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-06-17 20:50:01 +00:00
|
|
|
alexa_config = MockConfig(hass)
|
2019-06-13 18:58:08 +00:00
|
|
|
alexa_config.should_expose = entityfilter.generate_filter(
|
|
|
|
include_domains=[],
|
|
|
|
include_entities=[],
|
2019-07-31 19:25:30 +00:00
|
|
|
exclude_domains=["script"],
|
|
|
|
exclude_entities=["cover.deny"],
|
2019-06-13 18:58:08 +00:00
|
|
|
)
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-06-13 15:43:57 +00:00
|
|
|
msg = await smart_home.async_handle_message(hass, alexa_config, request)
|
2018-10-29 21:57:27 +00:00
|
|
|
await hass.async_block_till_done()
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
msg = msg["event"]
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert len(msg["payload"]["endpoints"]) == 1
|
2017-11-18 05:10:24 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_include_filters(hass):
|
2017-11-18 05:10:24 +00:00
|
|
|
"""Test inclusion filters."""
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.Discovery", "Discover")
|
2017-11-18 05:10:24 +00:00
|
|
|
|
|
|
|
# setup test devices
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.states.async_set("switch.deny", "on", {"friendly_name": "Blocked switch"})
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.states.async_set("script.deny", "off", {"friendly_name": "Blocked script"})
|
2017-11-18 05:10:24 +00:00
|
|
|
|
|
|
|
hass.states.async_set(
|
2019-07-31 19:25:30 +00:00
|
|
|
"automation.allow", "off", {"friendly_name": "Allowed automation"}
|
|
|
|
)
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.states.async_set("group.allow", "off", {"friendly_name": "Allowed group"})
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-06-17 20:50:01 +00:00
|
|
|
alexa_config = MockConfig(hass)
|
2019-06-13 18:58:08 +00:00
|
|
|
alexa_config.should_expose = entityfilter.generate_filter(
|
2019-07-31 19:25:30 +00:00
|
|
|
include_domains=["automation", "group"],
|
|
|
|
include_entities=["script.deny"],
|
2019-06-13 18:58:08 +00:00
|
|
|
exclude_domains=[],
|
|
|
|
exclude_entities=[],
|
|
|
|
)
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-06-13 15:43:57 +00:00
|
|
|
msg = await smart_home.async_handle_message(hass, alexa_config, request)
|
2018-10-29 21:57:27 +00:00
|
|
|
await hass.async_block_till_done()
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
msg = msg["event"]
|
2017-11-18 05:10:24 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert len(msg["payload"]["endpoints"]) == 3
|
2017-11-18 05:10:24 +00:00
|
|
|
|
|
|
|
|
Add support for locks in google assistant component (#18233)
* Add support for locks in google assistant component
This is supported by the smarthome API, but there is no documentation
for it. This work is based on an article I found with screenshots of
documentation that was erroneously uploaded:
https://www.androidpolice.com/2018/01/17/google-assistant-home-can-now-natively-control-smart-locks-august-vivint-first-supported/
Google Assistant now supports unlocking certain locks - Nest and August
come to mind - via this API, and this commit allows Home Assistant to
do so as well.
Notably, I've added a config option `allow_unlock` that controls
whether we actually honor requests to unlock a lock via the google
assistant. It defaults to false.
Additionally, we add the functionNotSupported error, which makes a
little more sense when we're unable to execute the desired state
transition.
https://developers.google.com/actions/reference/smarthome/errors-exceptions#exception_list
* Fix linter warnings
* Ensure that certain groups are never exposed to cloud entities
For example, the group.all_locks entity - we should probably never
expose this to third party cloud integrations. It's risky.
This is not configurable, but can be extended by adding to the
cloud.const.NEVER_EXPOSED_ENTITIES array.
It's implemented in a modestly hacky fashion, because we determine
whether or not a entity should be excluded/included in several ways.
Notably, we define this array in the top level const.py, to avoid
circular import problems between the cloud/alexa components.
2018-11-06 09:39:10 +00:00
|
|
|
async def test_never_exposed_entities(hass):
|
|
|
|
"""Test never exposed locks do not get discovered."""
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.Discovery", "Discover")
|
Add support for locks in google assistant component (#18233)
* Add support for locks in google assistant component
This is supported by the smarthome API, but there is no documentation
for it. This work is based on an article I found with screenshots of
documentation that was erroneously uploaded:
https://www.androidpolice.com/2018/01/17/google-assistant-home-can-now-natively-control-smart-locks-august-vivint-first-supported/
Google Assistant now supports unlocking certain locks - Nest and August
come to mind - via this API, and this commit allows Home Assistant to
do so as well.
Notably, I've added a config option `allow_unlock` that controls
whether we actually honor requests to unlock a lock via the google
assistant. It defaults to false.
Additionally, we add the functionNotSupported error, which makes a
little more sense when we're unable to execute the desired state
transition.
https://developers.google.com/actions/reference/smarthome/errors-exceptions#exception_list
* Fix linter warnings
* Ensure that certain groups are never exposed to cloud entities
For example, the group.all_locks entity - we should probably never
expose this to third party cloud integrations. It's risky.
This is not configurable, but can be extended by adding to the
cloud.const.NEVER_EXPOSED_ENTITIES array.
It's implemented in a modestly hacky fashion, because we determine
whether or not a entity should be excluded/included in several ways.
Notably, we define this array in the top level const.py, to avoid
circular import problems between the cloud/alexa components.
2018-11-06 09:39:10 +00:00
|
|
|
|
|
|
|
# setup test devices
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.states.async_set("group.all_locks", "on", {"friendly_name": "Blocked locks"})
|
Add support for locks in google assistant component (#18233)
* Add support for locks in google assistant component
This is supported by the smarthome API, but there is no documentation
for it. This work is based on an article I found with screenshots of
documentation that was erroneously uploaded:
https://www.androidpolice.com/2018/01/17/google-assistant-home-can-now-natively-control-smart-locks-august-vivint-first-supported/
Google Assistant now supports unlocking certain locks - Nest and August
come to mind - via this API, and this commit allows Home Assistant to
do so as well.
Notably, I've added a config option `allow_unlock` that controls
whether we actually honor requests to unlock a lock via the google
assistant. It defaults to false.
Additionally, we add the functionNotSupported error, which makes a
little more sense when we're unable to execute the desired state
transition.
https://developers.google.com/actions/reference/smarthome/errors-exceptions#exception_list
* Fix linter warnings
* Ensure that certain groups are never exposed to cloud entities
For example, the group.all_locks entity - we should probably never
expose this to third party cloud integrations. It's risky.
This is not configurable, but can be extended by adding to the
cloud.const.NEVER_EXPOSED_ENTITIES array.
It's implemented in a modestly hacky fashion, because we determine
whether or not a entity should be excluded/included in several ways.
Notably, we define this array in the top level const.py, to avoid
circular import problems between the cloud/alexa components.
2018-11-06 09:39:10 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.states.async_set("group.allow", "off", {"friendly_name": "Allowed group"})
|
Add support for locks in google assistant component (#18233)
* Add support for locks in google assistant component
This is supported by the smarthome API, but there is no documentation
for it. This work is based on an article I found with screenshots of
documentation that was erroneously uploaded:
https://www.androidpolice.com/2018/01/17/google-assistant-home-can-now-natively-control-smart-locks-august-vivint-first-supported/
Google Assistant now supports unlocking certain locks - Nest and August
come to mind - via this API, and this commit allows Home Assistant to
do so as well.
Notably, I've added a config option `allow_unlock` that controls
whether we actually honor requests to unlock a lock via the google
assistant. It defaults to false.
Additionally, we add the functionNotSupported error, which makes a
little more sense when we're unable to execute the desired state
transition.
https://developers.google.com/actions/reference/smarthome/errors-exceptions#exception_list
* Fix linter warnings
* Ensure that certain groups are never exposed to cloud entities
For example, the group.all_locks entity - we should probably never
expose this to third party cloud integrations. It's risky.
This is not configurable, but can be extended by adding to the
cloud.const.NEVER_EXPOSED_ENTITIES array.
It's implemented in a modestly hacky fashion, because we determine
whether or not a entity should be excluded/included in several ways.
Notably, we define this array in the top level const.py, to avoid
circular import problems between the cloud/alexa components.
2018-11-06 09:39:10 +00:00
|
|
|
|
2019-06-17 20:50:01 +00:00
|
|
|
alexa_config = MockConfig(hass)
|
2019-06-13 18:58:08 +00:00
|
|
|
alexa_config.should_expose = entityfilter.generate_filter(
|
2019-07-31 19:25:30 +00:00
|
|
|
include_domains=["group"],
|
2019-06-13 18:58:08 +00:00
|
|
|
include_entities=[],
|
|
|
|
exclude_domains=[],
|
|
|
|
exclude_entities=[],
|
|
|
|
)
|
Add support for locks in google assistant component (#18233)
* Add support for locks in google assistant component
This is supported by the smarthome API, but there is no documentation
for it. This work is based on an article I found with screenshots of
documentation that was erroneously uploaded:
https://www.androidpolice.com/2018/01/17/google-assistant-home-can-now-natively-control-smart-locks-august-vivint-first-supported/
Google Assistant now supports unlocking certain locks - Nest and August
come to mind - via this API, and this commit allows Home Assistant to
do so as well.
Notably, I've added a config option `allow_unlock` that controls
whether we actually honor requests to unlock a lock via the google
assistant. It defaults to false.
Additionally, we add the functionNotSupported error, which makes a
little more sense when we're unable to execute the desired state
transition.
https://developers.google.com/actions/reference/smarthome/errors-exceptions#exception_list
* Fix linter warnings
* Ensure that certain groups are never exposed to cloud entities
For example, the group.all_locks entity - we should probably never
expose this to third party cloud integrations. It's risky.
This is not configurable, but can be extended by adding to the
cloud.const.NEVER_EXPOSED_ENTITIES array.
It's implemented in a modestly hacky fashion, because we determine
whether or not a entity should be excluded/included in several ways.
Notably, we define this array in the top level const.py, to avoid
circular import problems between the cloud/alexa components.
2018-11-06 09:39:10 +00:00
|
|
|
|
2019-06-13 15:43:57 +00:00
|
|
|
msg = await smart_home.async_handle_message(hass, alexa_config, request)
|
Add support for locks in google assistant component (#18233)
* Add support for locks in google assistant component
This is supported by the smarthome API, but there is no documentation
for it. This work is based on an article I found with screenshots of
documentation that was erroneously uploaded:
https://www.androidpolice.com/2018/01/17/google-assistant-home-can-now-natively-control-smart-locks-august-vivint-first-supported/
Google Assistant now supports unlocking certain locks - Nest and August
come to mind - via this API, and this commit allows Home Assistant to
do so as well.
Notably, I've added a config option `allow_unlock` that controls
whether we actually honor requests to unlock a lock via the google
assistant. It defaults to false.
Additionally, we add the functionNotSupported error, which makes a
little more sense when we're unable to execute the desired state
transition.
https://developers.google.com/actions/reference/smarthome/errors-exceptions#exception_list
* Fix linter warnings
* Ensure that certain groups are never exposed to cloud entities
For example, the group.all_locks entity - we should probably never
expose this to third party cloud integrations. It's risky.
This is not configurable, but can be extended by adding to the
cloud.const.NEVER_EXPOSED_ENTITIES array.
It's implemented in a modestly hacky fashion, because we determine
whether or not a entity should be excluded/included in several ways.
Notably, we define this array in the top level const.py, to avoid
circular import problems between the cloud/alexa components.
2018-11-06 09:39:10 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
msg = msg["event"]
|
Add support for locks in google assistant component (#18233)
* Add support for locks in google assistant component
This is supported by the smarthome API, but there is no documentation
for it. This work is based on an article I found with screenshots of
documentation that was erroneously uploaded:
https://www.androidpolice.com/2018/01/17/google-assistant-home-can-now-natively-control-smart-locks-august-vivint-first-supported/
Google Assistant now supports unlocking certain locks - Nest and August
come to mind - via this API, and this commit allows Home Assistant to
do so as well.
Notably, I've added a config option `allow_unlock` that controls
whether we actually honor requests to unlock a lock via the google
assistant. It defaults to false.
Additionally, we add the functionNotSupported error, which makes a
little more sense when we're unable to execute the desired state
transition.
https://developers.google.com/actions/reference/smarthome/errors-exceptions#exception_list
* Fix linter warnings
* Ensure that certain groups are never exposed to cloud entities
For example, the group.all_locks entity - we should probably never
expose this to third party cloud integrations. It's risky.
This is not configurable, but can be extended by adding to the
cloud.const.NEVER_EXPOSED_ENTITIES array.
It's implemented in a modestly hacky fashion, because we determine
whether or not a entity should be excluded/included in several ways.
Notably, we define this array in the top level const.py, to avoid
circular import problems between the cloud/alexa components.
2018-11-06 09:39:10 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert len(msg["payload"]["endpoints"]) == 1
|
Add support for locks in google assistant component (#18233)
* Add support for locks in google assistant component
This is supported by the smarthome API, but there is no documentation
for it. This work is based on an article I found with screenshots of
documentation that was erroneously uploaded:
https://www.androidpolice.com/2018/01/17/google-assistant-home-can-now-natively-control-smart-locks-august-vivint-first-supported/
Google Assistant now supports unlocking certain locks - Nest and August
come to mind - via this API, and this commit allows Home Assistant to
do so as well.
Notably, I've added a config option `allow_unlock` that controls
whether we actually honor requests to unlock a lock via the google
assistant. It defaults to false.
Additionally, we add the functionNotSupported error, which makes a
little more sense when we're unable to execute the desired state
transition.
https://developers.google.com/actions/reference/smarthome/errors-exceptions#exception_list
* Fix linter warnings
* Ensure that certain groups are never exposed to cloud entities
For example, the group.all_locks entity - we should probably never
expose this to third party cloud integrations. It's risky.
This is not configurable, but can be extended by adding to the
cloud.const.NEVER_EXPOSED_ENTITIES array.
It's implemented in a modestly hacky fashion, because we determine
whether or not a entity should be excluded/included in several ways.
Notably, we define this array in the top level const.py, to avoid
circular import problems between the cloud/alexa components.
2018-11-06 09:39:10 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_api_entity_not_exists(hass):
|
2017-09-16 19:35:28 +00:00
|
|
|
"""Test api turn on process without entity."""
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.PowerController", "TurnOn", "switch#test")
|
2017-09-16 19:35:28 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
call_switch = async_mock_service(hass, "switch", "turn_on")
|
2017-09-16 19:35:28 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request)
|
2018-10-29 21:57:27 +00:00
|
|
|
await hass.async_block_till_done()
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2018-01-30 04:33:39 +00:00
|
|
|
assert not call_switch
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["header"]["name"] == "ErrorResponse"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa"
|
|
|
|
assert msg["payload"]["type"] == "NO_SUCH_ENDPOINT"
|
2017-10-07 20:31:57 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_api_function_not_implemented(hass):
|
2017-10-07 20:31:57 +00:00
|
|
|
"""Test api call that is not implemented to us."""
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.HAHAAH", "Sweet")
|
|
|
|
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request)
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
2017-10-07 20:31:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["header"]["name"] == "ErrorResponse"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa"
|
|
|
|
assert msg["payload"]["type"] == "INTERNAL_ERROR"
|
2017-09-16 19:35:28 +00:00
|
|
|
|
|
|
|
|
2019-01-03 21:28:43 +00:00
|
|
|
async def test_api_accept_grant(hass):
|
|
|
|
"""Test api AcceptGrant process."""
|
|
|
|
request = get_new_request("Alexa.Authorization", "AcceptGrant")
|
|
|
|
|
|
|
|
# add payload
|
2019-07-31 19:25:30 +00:00
|
|
|
request["directive"]["payload"] = {
|
|
|
|
"grant": {
|
|
|
|
"type": "OAuth2.AuthorizationCode",
|
|
|
|
"code": "VGhpcyBpcyBhbiBhdXRob3JpemF0aW9uIGNvZGUuIDotKQ==",
|
2019-06-13 15:43:57 +00:00
|
|
|
},
|
2019-07-31 19:25:30 +00:00
|
|
|
"grantee": {"type": "BearerToken", "token": "access-token-from-skill"},
|
2019-01-03 21:28:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# setup test devices
|
2019-07-31 19:25:30 +00:00
|
|
|
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request)
|
2019-01-03 21:28:43 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
2019-01-03 21:28:43 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["header"]["name"] == "AcceptGrant.Response"
|
2019-01-03 21:28:43 +00:00
|
|
|
|
|
|
|
|
2018-10-29 21:57:27 +00:00
|
|
|
async def test_entity_config(hass):
|
2018-01-30 04:33:39 +00:00
|
|
|
"""Test that we can configure things via entity config."""
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.Discovery", "Discover")
|
2018-01-26 05:06:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.states.async_set("light.test_1", "on", {"friendly_name": "Test light 1"})
|
2019-09-27 19:51:46 +00:00
|
|
|
hass.states.async_set("scene.test_1", "scening", {"friendly_name": "Test 1"})
|
2018-01-26 05:06:57 +00:00
|
|
|
|
2019-06-17 20:50:01 +00:00
|
|
|
alexa_config = MockConfig(hass)
|
2019-06-13 18:58:08 +00:00
|
|
|
alexa_config.entity_config = {
|
2019-07-31 19:25:30 +00:00
|
|
|
"light.test_1": {
|
2019-09-27 19:51:46 +00:00
|
|
|
"name": "Config *name*",
|
2019-07-31 19:25:30 +00:00
|
|
|
"display_categories": "SWITCH",
|
2019-09-27 19:51:46 +00:00
|
|
|
"description": "Config >!<description",
|
|
|
|
},
|
|
|
|
"scene.test_1": {"description": "Config description"},
|
2019-06-13 18:58:08 +00:00
|
|
|
}
|
2018-01-26 05:06:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
msg = await smart_home.async_handle_message(hass, alexa_config, request)
|
2018-01-26 05:06:57 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
2018-01-26 05:06:57 +00:00
|
|
|
|
2019-09-27 19:51:46 +00:00
|
|
|
assert len(msg["payload"]["endpoints"]) == 2
|
2018-01-05 20:33:22 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
appliance = msg["payload"]["endpoints"][0]
|
|
|
|
assert appliance["endpointId"] == "light#test_1"
|
|
|
|
assert appliance["displayCategories"][0] == "SWITCH"
|
|
|
|
assert appliance["friendlyName"] == "Config name"
|
2019-09-27 19:51:46 +00:00
|
|
|
assert appliance["description"] == "Config description via Home Assistant"
|
2019-01-10 23:52:21 +00:00
|
|
|
assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
2018-01-23 18:45:28 +00:00
|
|
|
|
2019-09-27 19:51:46 +00:00
|
|
|
scene = msg["payload"]["endpoints"][1]
|
|
|
|
assert scene["endpointId"] == "scene#test_1"
|
|
|
|
assert scene["displayCategories"][0] == "SCENE_TRIGGER"
|
|
|
|
assert scene["friendlyName"] == "Test 1"
|
|
|
|
assert scene["description"] == "Config description via Home Assistant (Scene)"
|
|
|
|
|
2018-01-23 18:45:28 +00:00
|
|
|
|
2018-08-20 12:18:07 +00:00
|
|
|
async def test_logging_request(hass, events):
|
|
|
|
"""Test that we log requests."""
|
|
|
|
context = Context()
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.Discovery", "Discover")
|
|
|
|
await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request, context)
|
2018-08-20 12:18:07 +00:00
|
|
|
|
|
|
|
# To trigger event listener
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(events) == 1
|
|
|
|
event = events[0]
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert event.data["request"] == {"namespace": "Alexa.Discovery", "name": "Discover"}
|
|
|
|
assert event.data["response"] == {
|
|
|
|
"namespace": "Alexa.Discovery",
|
|
|
|
"name": "Discover.Response",
|
2018-08-20 12:18:07 +00:00
|
|
|
}
|
|
|
|
assert event.context == context
|
|
|
|
|
|
|
|
|
|
|
|
async def test_logging_request_with_entity(hass, events):
|
|
|
|
"""Test that we log requests."""
|
|
|
|
context = Context()
|
2019-07-31 19:25:30 +00:00
|
|
|
request = get_new_request("Alexa.PowerController", "TurnOn", "switch#xy")
|
|
|
|
await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request, context)
|
2018-08-20 12:18:07 +00:00
|
|
|
|
|
|
|
# To trigger event listener
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
assert len(events) == 1
|
|
|
|
event = events[0]
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert event.data["request"] == {
|
|
|
|
"namespace": "Alexa.PowerController",
|
|
|
|
"name": "TurnOn",
|
|
|
|
"entity_id": "switch.xy",
|
2018-08-20 12:18:07 +00:00
|
|
|
}
|
|
|
|
# Entity doesn't exist
|
2019-07-31 19:25:30 +00:00
|
|
|
assert event.data["response"] == {"namespace": "Alexa", "name": "ErrorResponse"}
|
2018-08-20 12:18:07 +00:00
|
|
|
assert event.context == context
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_disabled(hass):
|
|
|
|
"""When enabled=False, everything fails."""
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.states.async_set("switch.test", "on", {"friendly_name": "Test switch"})
|
|
|
|
request = get_new_request("Alexa.PowerController", "TurnOn", "switch#test")
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
call_switch = async_mock_service(hass, "switch", "turn_on")
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
|
|
|
|
msg = await smart_home.async_handle_message(
|
2019-07-31 19:25:30 +00:00
|
|
|
hass, DEFAULT_CONFIG, request, enabled=False
|
|
|
|
)
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
Refactor Alexa API, fix thermostats (#17969)
* Refactor Alexa API to use objects for requests
This introduces _AlexaDirective to stand in for the previous model of passing
basic dict and list data structures to and from handlers. This gives a more
expressive platform for functionality common to most or all handlers.
I had two use cases in mind:
1) Most responses should include current properties. In the case of locks and
thermostats, the response must include the properties or Alexa will give the
user a vague error like "Hmm, $device is not responding." Locks currently work,
but thermostats do not. I wanted a way to automatically include properties in
all responses. This is implemented in a subsequent commit.
2) The previous model had a 1:1 mapping between Alexa endpoints and Home
Assistant entities. This works most of the time, but sometimes it's not so
great. For example, my Z-wave thermostat shows as three devices in Alexa: one
for the temperature sensor, one for the heat, and one for the AC. I'd like to
merge these into one device from Alexa's perspective. I believe this will be
facilitated with the `endpoint` attribute on `_AlexaDirective`.
* Include properties in all Alexa responses
The added _AlexaResponse class provides a richer vocabulary for handlers.
Among that vocabulary is .merge_context_properties(), which is invoked
automatically for any request directed at an endpoint. This adds all supported
properties to the response as recommended by the Alexa API docs, and in some
cases (locks, thermostats at least) the user will get an error "Hmm, $device is
not responding" if properties are not provided in the response.
* Fix setting temperature with Alexa thermostats
Fixes https://github.com/home-assistant/home-assistant/issues/16577
2018-10-30 02:16:35 +00:00
|
|
|
|
|
|
|
assert not call_switch
|
2019-07-31 19:25:30 +00:00
|
|
|
assert msg["header"]["name"] == "ErrorResponse"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa"
|
|
|
|
assert msg["payload"]["type"] == "BRIDGE_UNREACHABLE"
|
2019-01-03 21:28:43 +00:00
|
|
|
|
|
|
|
|
2019-01-10 23:52:21 +00:00
|
|
|
async def test_endpoint_good_health(hass):
|
|
|
|
"""Test endpoint health reporting."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"binary_sensor.test_contact",
|
|
|
|
"on",
|
|
|
|
{"friendly_name": "Test Contact Sensor", "device_class": "door"},
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
|
|
|
await discovery_test(device, hass)
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = await reported_properties(hass, "binary_sensor#test_contact")
|
|
|
|
properties.assert_equal("Alexa.EndpointHealth", "connectivity", {"value": "OK"})
|
2019-01-10 23:52:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_endpoint_bad_health(hass):
|
|
|
|
"""Test endpoint health reporting."""
|
|
|
|
device = (
|
2019-07-31 19:25:30 +00:00
|
|
|
"binary_sensor.test_contact",
|
|
|
|
"unavailable",
|
|
|
|
{"friendly_name": "Test Contact Sensor", "device_class": "door"},
|
2019-01-10 23:52:21 +00:00
|
|
|
)
|
|
|
|
await discovery_test(device, hass)
|
2019-07-31 19:25:30 +00:00
|
|
|
properties = await reported_properties(hass, "binary_sensor#test_contact")
|
|
|
|
properties.assert_equal(
|
|
|
|
"Alexa.EndpointHealth", "connectivity", {"value": "UNREACHABLE"}
|
|
|
|
)
|
2019-10-04 15:41:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_alarm_control_panel_disarmed(hass):
|
|
|
|
"""Test alarm_control_panel discovery."""
|
|
|
|
device = (
|
|
|
|
"alarm_control_panel.test_1",
|
|
|
|
"disarmed",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test Alarm Control Panel 1",
|
|
|
|
"code_arm_required": False,
|
|
|
|
"code_format": "number",
|
|
|
|
"code": "1234",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "alarm_control_panel#test_1"
|
|
|
|
assert appliance["displayCategories"][0] == "SECURITY_PANEL"
|
|
|
|
assert appliance["friendlyName"] == "Test Alarm Control Panel 1"
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.SecurityPanelController", "Alexa.EndpointHealth", "Alexa"
|
2019-10-04 15:41:47 +00:00
|
|
|
)
|
|
|
|
security_panel_capability = get_capability(
|
|
|
|
capabilities, "Alexa.SecurityPanelController"
|
|
|
|
)
|
|
|
|
assert security_panel_capability is not None
|
|
|
|
configuration = security_panel_capability["configuration"]
|
|
|
|
assert {"type": "FOUR_DIGIT_PIN"} in configuration["supportedAuthorizationTypes"]
|
|
|
|
|
|
|
|
properties = await reported_properties(hass, "alarm_control_panel#test_1")
|
|
|
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "DISARMED")
|
|
|
|
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.SecurityPanelController",
|
|
|
|
"Arm",
|
|
|
|
"alarm_control_panel#test_1",
|
|
|
|
"alarm_control_panel.alarm_arm_home",
|
|
|
|
hass,
|
|
|
|
response_type="Arm.Response",
|
|
|
|
payload={"armState": "ARMED_STAY"},
|
|
|
|
)
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
|
|
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_STAY")
|
|
|
|
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.SecurityPanelController",
|
|
|
|
"Arm",
|
|
|
|
"alarm_control_panel#test_1",
|
|
|
|
"alarm_control_panel.alarm_arm_away",
|
|
|
|
hass,
|
|
|
|
response_type="Arm.Response",
|
|
|
|
payload={"armState": "ARMED_AWAY"},
|
|
|
|
)
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
|
|
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_AWAY")
|
|
|
|
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.SecurityPanelController",
|
|
|
|
"Arm",
|
|
|
|
"alarm_control_panel#test_1",
|
|
|
|
"alarm_control_panel.alarm_arm_night",
|
|
|
|
hass,
|
|
|
|
response_type="Arm.Response",
|
|
|
|
payload={"armState": "ARMED_NIGHT"},
|
|
|
|
)
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
|
|
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_NIGHT")
|
|
|
|
|
|
|
|
|
|
|
|
async def test_alarm_control_panel_armed(hass):
|
|
|
|
"""Test alarm_control_panel discovery."""
|
|
|
|
device = (
|
|
|
|
"alarm_control_panel.test_2",
|
|
|
|
"armed_away",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test Alarm Control Panel 2",
|
|
|
|
"code_arm_required": False,
|
|
|
|
"code_format": "FORMAT_NUMBER",
|
|
|
|
"code": "1234",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "alarm_control_panel#test_2"
|
|
|
|
assert appliance["displayCategories"][0] == "SECURITY_PANEL"
|
|
|
|
assert appliance["friendlyName"] == "Test Alarm Control Panel 2"
|
|
|
|
assert_endpoint_capabilities(
|
2019-11-26 07:05:10 +00:00
|
|
|
appliance, "Alexa.SecurityPanelController", "Alexa.EndpointHealth", "Alexa"
|
2019-10-04 15:41:47 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
properties = await reported_properties(hass, "alarm_control_panel#test_2")
|
|
|
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "ARMED_AWAY")
|
|
|
|
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.SecurityPanelController",
|
|
|
|
"Disarm",
|
|
|
|
"alarm_control_panel#test_2",
|
|
|
|
"alarm_control_panel.alarm_disarm",
|
|
|
|
hass,
|
|
|
|
payload={"authorization": {"type": "FOUR_DIGIT_PIN", "value": "1234"}},
|
|
|
|
)
|
|
|
|
assert call.data["code"] == "1234"
|
|
|
|
properties = ReportedProperties(msg["context"]["properties"])
|
|
|
|
properties.assert_equal("Alexa.SecurityPanelController", "armState", "DISARMED")
|
|
|
|
|
|
|
|
msg = await assert_request_fails(
|
|
|
|
"Alexa.SecurityPanelController",
|
|
|
|
"Arm",
|
|
|
|
"alarm_control_panel#test_2",
|
|
|
|
"alarm_control_panel.alarm_arm_home",
|
|
|
|
hass,
|
|
|
|
payload={"armState": "ARMED_STAY"},
|
|
|
|
)
|
|
|
|
assert msg["event"]["payload"]["type"] == "AUTHORIZATION_REQUIRED"
|
|
|
|
|
|
|
|
|
|
|
|
async def test_alarm_control_panel_code_arm_required(hass):
|
|
|
|
"""Test alarm_control_panel with code_arm_required discovery."""
|
|
|
|
device = (
|
|
|
|
"alarm_control_panel.test_3",
|
|
|
|
"disarmed",
|
|
|
|
{"friendly_name": "Test Alarm Control Panel 3", "code_arm_required": True},
|
|
|
|
)
|
|
|
|
await discovery_test(device, hass, expected_endpoints=0)
|
2019-10-23 05:01:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_range_unsupported_domain(hass):
|
|
|
|
"""Test rangeController with unsupported domain."""
|
|
|
|
device = ("switch.test", "on", {"friendly_name": "Test switch"})
|
|
|
|
await discovery_test(device, hass)
|
|
|
|
|
|
|
|
context = Context()
|
|
|
|
request = get_new_request("Alexa.RangeController", "SetRangeValue", "switch#test")
|
|
|
|
request["directive"]["payload"] = {"rangeValue": "1"}
|
|
|
|
request["directive"]["header"]["instance"] = "switch.speed"
|
|
|
|
|
|
|
|
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request, context)
|
|
|
|
|
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
|
|
|
assert msg["header"]["name"] == "ErrorResponse"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa"
|
|
|
|
assert msg["payload"]["type"] == "INVALID_DIRECTIVE"
|
|
|
|
|
|
|
|
|
|
|
|
async def test_mode_unsupported_domain(hass):
|
|
|
|
"""Test modeController with unsupported domain."""
|
|
|
|
device = ("switch.test", "on", {"friendly_name": "Test switch"})
|
|
|
|
await discovery_test(device, hass)
|
|
|
|
|
|
|
|
context = Context()
|
|
|
|
request = get_new_request("Alexa.ModeController", "SetMode", "switch#test")
|
|
|
|
request["directive"]["payload"] = {"mode": "testMode"}
|
|
|
|
request["directive"]["header"]["instance"] = "switch.direction"
|
|
|
|
|
|
|
|
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request, context)
|
|
|
|
|
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
|
|
|
assert msg["header"]["name"] == "ErrorResponse"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa"
|
|
|
|
assert msg["payload"]["type"] == "INVALID_DIRECTIVE"
|
2019-11-25 23:07:33 +00:00
|
|
|
|
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
async def test_cover_position_mode(hass):
|
|
|
|
"""Test cover discovery and position using modeController."""
|
2019-11-25 23:07:33 +00:00
|
|
|
device = (
|
2019-12-19 11:44:17 +00:00
|
|
|
"cover.test_mode",
|
|
|
|
"open",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test cover mode",
|
|
|
|
"device_class": "blind",
|
|
|
|
"supported_features": 3,
|
|
|
|
},
|
2019-11-25 23:07:33 +00:00
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
assert appliance["endpointId"] == "cover#test_mode"
|
|
|
|
assert appliance["displayCategories"][0] == "INTERIOR_BLIND"
|
|
|
|
assert appliance["friendlyName"] == "Test cover mode"
|
2019-11-25 23:07:33 +00:00
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
2019-12-19 11:44:17 +00:00
|
|
|
appliance, "Alexa", "Alexa.ModeController", "Alexa.EndpointHealth"
|
2019-11-25 23:07:33 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
mode_capability = get_capability(capabilities, "Alexa.ModeController")
|
|
|
|
assert mode_capability is not None
|
|
|
|
assert mode_capability["instance"] == "cover.position"
|
|
|
|
|
|
|
|
properties = mode_capability["properties"]
|
|
|
|
assert properties["nonControllable"] is False
|
|
|
|
assert {"name": "mode"} in properties["supported"]
|
|
|
|
|
|
|
|
capability_resources = mode_capability["capabilityResources"]
|
|
|
|
assert capability_resources is not None
|
2019-12-19 11:44:17 +00:00
|
|
|
assert {
|
|
|
|
"@type": "text",
|
|
|
|
"value": {"text": "Position", "locale": "en-US"},
|
|
|
|
} in capability_resources["friendlyNames"]
|
|
|
|
|
2019-11-25 23:07:33 +00:00
|
|
|
assert {
|
|
|
|
"@type": "asset",
|
2019-12-19 11:44:17 +00:00
|
|
|
"value": {"assetId": "Alexa.Setting.Opening"},
|
2019-11-25 23:07:33 +00:00
|
|
|
} in capability_resources["friendlyNames"]
|
|
|
|
|
|
|
|
configuration = mode_capability["configuration"]
|
|
|
|
assert configuration is not None
|
|
|
|
assert configuration["ordered"] is False
|
|
|
|
|
|
|
|
supported_modes = configuration["supportedModes"]
|
|
|
|
assert supported_modes is not None
|
|
|
|
assert {
|
|
|
|
"value": "position.open",
|
|
|
|
"modeResources": {
|
|
|
|
"friendlyNames": [
|
2019-12-19 11:44:17 +00:00
|
|
|
{"@type": "asset", "value": {"assetId": "Alexa.Value.Open"}}
|
2019-11-25 23:07:33 +00:00
|
|
|
]
|
|
|
|
},
|
|
|
|
} in supported_modes
|
|
|
|
assert {
|
|
|
|
"value": "position.closed",
|
|
|
|
"modeResources": {
|
|
|
|
"friendlyNames": [
|
2019-12-19 11:44:17 +00:00
|
|
|
{"@type": "asset", "value": {"assetId": "Alexa.Value.Close"}}
|
2019-11-25 23:07:33 +00:00
|
|
|
]
|
|
|
|
},
|
|
|
|
} in supported_modes
|
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
semantics = mode_capability["semantics"]
|
|
|
|
assert semantics is not None
|
|
|
|
|
|
|
|
action_mappings = semantics["actionMappings"]
|
|
|
|
assert action_mappings is not None
|
|
|
|
|
|
|
|
state_mappings = semantics["stateMappings"]
|
|
|
|
assert state_mappings is not None
|
|
|
|
|
2019-11-25 23:07:33 +00:00
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.ModeController",
|
|
|
|
"SetMode",
|
2019-12-19 11:44:17 +00:00
|
|
|
"cover#test_mode",
|
2019-11-25 23:07:33 +00:00
|
|
|
"cover.close_cover",
|
|
|
|
hass,
|
|
|
|
payload={"mode": "position.closed"},
|
|
|
|
instance="cover.position",
|
|
|
|
)
|
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "mode"
|
|
|
|
assert properties["namespace"] == "Alexa.ModeController"
|
|
|
|
assert properties["value"] == "position.closed"
|
|
|
|
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.ModeController",
|
|
|
|
"SetMode",
|
2019-12-19 11:44:17 +00:00
|
|
|
"cover#test_mode",
|
2019-11-25 23:07:33 +00:00
|
|
|
"cover.open_cover",
|
|
|
|
hass,
|
|
|
|
payload={"mode": "position.open"},
|
|
|
|
instance="cover.position",
|
|
|
|
)
|
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "mode"
|
|
|
|
assert properties["namespace"] == "Alexa.ModeController"
|
|
|
|
assert properties["value"] == "position.open"
|
2019-12-14 07:47:45 +00:00
|
|
|
|
2019-12-19 11:44:17 +00:00
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.ModeController",
|
|
|
|
"SetMode",
|
|
|
|
"cover#test_mode",
|
|
|
|
"cover.stop_cover",
|
|
|
|
hass,
|
|
|
|
payload={"mode": "position.custom"},
|
|
|
|
instance="cover.position",
|
|
|
|
)
|
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "mode"
|
|
|
|
assert properties["namespace"] == "Alexa.ModeController"
|
|
|
|
assert properties["value"] == "position.custom"
|
|
|
|
|
2019-12-14 07:47:45 +00:00
|
|
|
|
|
|
|
async def test_image_processing(hass):
|
|
|
|
"""Test image_processing discovery as event detection."""
|
|
|
|
device = (
|
|
|
|
"image_processing.test_face",
|
|
|
|
0,
|
|
|
|
{
|
|
|
|
"friendly_name": "Test face",
|
|
|
|
"device_class": "face",
|
|
|
|
"faces": [],
|
|
|
|
"total_faces": 0,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "image_processing#test_face"
|
|
|
|
assert appliance["displayCategories"][0] == "CAMERA"
|
|
|
|
assert appliance["friendlyName"] == "Test face"
|
|
|
|
|
|
|
|
assert_endpoint_capabilities(
|
2019-12-20 19:28:23 +00:00
|
|
|
appliance, "Alexa.EventDetectionSensor", "Alexa.EndpointHealth", "Alexa"
|
2019-12-14 07:47:45 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_motion_sensor_event_detection(hass):
|
|
|
|
"""Test motion sensor with EventDetectionSensor discovery."""
|
|
|
|
device = (
|
|
|
|
"binary_sensor.test_motion_camera_event",
|
|
|
|
"off",
|
|
|
|
{"friendly_name": "Test motion camera event", "device_class": "motion"},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "binary_sensor#test_motion_camera_event"
|
|
|
|
assert appliance["displayCategories"][0] == "CAMERA"
|
|
|
|
assert appliance["friendlyName"] == "Test motion camera event"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance,
|
|
|
|
"Alexa",
|
|
|
|
"Alexa.MotionSensor",
|
|
|
|
"Alexa.EventDetectionSensor",
|
|
|
|
"Alexa.EndpointHealth",
|
|
|
|
)
|
|
|
|
|
|
|
|
event_detection_capability = get_capability(
|
|
|
|
capabilities, "Alexa.EventDetectionSensor"
|
|
|
|
)
|
|
|
|
assert event_detection_capability is not None
|
|
|
|
properties = event_detection_capability["properties"]
|
|
|
|
assert properties["proactivelyReported"] is True
|
|
|
|
assert not properties["retrievable"]
|
|
|
|
assert {"name": "humanPresenceDetectionState"} in properties["supported"]
|
|
|
|
|
|
|
|
|
|
|
|
async def test_presence_sensor(hass):
|
|
|
|
"""Test presence sensor."""
|
|
|
|
device = (
|
|
|
|
"binary_sensor.test_presence_sensor",
|
|
|
|
"off",
|
|
|
|
{"friendly_name": "Test presence sensor", "device_class": "presence"},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "binary_sensor#test_presence_sensor"
|
|
|
|
assert appliance["displayCategories"][0] == "CAMERA"
|
|
|
|
assert appliance["friendlyName"] == "Test presence sensor"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa", "Alexa.EventDetectionSensor", "Alexa.EndpointHealth"
|
|
|
|
)
|
|
|
|
|
|
|
|
event_detection_capability = get_capability(
|
|
|
|
capabilities, "Alexa.EventDetectionSensor"
|
|
|
|
)
|
|
|
|
assert event_detection_capability is not None
|
|
|
|
properties = event_detection_capability["properties"]
|
|
|
|
assert properties["proactivelyReported"] is True
|
|
|
|
assert not properties["retrievable"]
|
|
|
|
assert {"name": "humanPresenceDetectionState"} in properties["supported"]
|
2019-12-19 11:44:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_cover_tilt_position_range(hass):
|
|
|
|
"""Test cover discovery and tilt position using rangeController."""
|
|
|
|
device = (
|
|
|
|
"cover.test_tilt_range",
|
|
|
|
"open",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test cover tilt range",
|
|
|
|
"device_class": "blind",
|
|
|
|
"supported_features": 240,
|
|
|
|
"tilt_position": 30,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "cover#test_tilt_range"
|
|
|
|
assert appliance["displayCategories"][0] == "INTERIOR_BLIND"
|
|
|
|
assert appliance["friendlyName"] == "Test cover tilt range"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa.RangeController", "Alexa.EndpointHealth", "Alexa"
|
|
|
|
)
|
|
|
|
|
|
|
|
range_capability = get_capability(capabilities, "Alexa.RangeController")
|
|
|
|
assert range_capability is not None
|
|
|
|
assert range_capability["instance"] == "cover.tilt_position"
|
|
|
|
|
|
|
|
semantics = range_capability["semantics"]
|
|
|
|
assert semantics is not None
|
|
|
|
|
|
|
|
action_mappings = semantics["actionMappings"]
|
|
|
|
assert action_mappings is not None
|
|
|
|
|
|
|
|
state_mappings = semantics["stateMappings"]
|
|
|
|
assert state_mappings is not None
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"SetRangeValue",
|
|
|
|
"cover#test_tilt_range",
|
|
|
|
"cover.set_cover_tilt_position",
|
|
|
|
hass,
|
|
|
|
payload={"rangeValue": "50"},
|
|
|
|
instance="cover.tilt_position",
|
|
|
|
)
|
|
|
|
assert call.data["position"] == 50
|
|
|
|
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"SetRangeValue",
|
|
|
|
"cover#test_tilt_range",
|
|
|
|
"cover.close_cover_tilt",
|
|
|
|
hass,
|
|
|
|
payload={"rangeValue": "0"},
|
|
|
|
instance="cover.tilt_position",
|
|
|
|
)
|
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "rangeValue"
|
|
|
|
assert properties["namespace"] == "Alexa.RangeController"
|
|
|
|
assert properties["value"] == 0
|
|
|
|
|
|
|
|
call, msg = await assert_request_calls_service(
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"SetRangeValue",
|
|
|
|
"cover#test_tilt_range",
|
|
|
|
"cover.open_cover_tilt",
|
|
|
|
hass,
|
|
|
|
payload={"rangeValue": "100"},
|
|
|
|
instance="cover.tilt_position",
|
|
|
|
)
|
|
|
|
properties = msg["context"]["properties"][0]
|
|
|
|
assert properties["name"] == "rangeValue"
|
|
|
|
assert properties["namespace"] == "Alexa.RangeController"
|
|
|
|
assert properties["value"] == 100
|
|
|
|
|
|
|
|
await assert_range_changes(
|
|
|
|
hass,
|
|
|
|
[(25, "-5"), (35, "5"), (0, "-99"), (100, "99")],
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"AdjustRangeValue",
|
|
|
|
"cover#test_tilt_range",
|
|
|
|
False,
|
|
|
|
"cover.set_cover_tilt_position",
|
|
|
|
"tilt_position",
|
|
|
|
instance="cover.tilt_position",
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_cover_semantics(hass):
|
|
|
|
"""Test cover discovery and semantics."""
|
|
|
|
device = (
|
|
|
|
"cover.test_semantics",
|
|
|
|
"open",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test cover semantics",
|
|
|
|
"device_class": "blind",
|
|
|
|
"supported_features": 255,
|
|
|
|
"position": 30,
|
|
|
|
"tilt_position": 30,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "cover#test_semantics"
|
|
|
|
assert appliance["displayCategories"][0] == "INTERIOR_BLIND"
|
|
|
|
assert appliance["friendlyName"] == "Test cover semantics"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa.RangeController", "Alexa.EndpointHealth", "Alexa"
|
|
|
|
)
|
|
|
|
|
|
|
|
for range_instance in ("cover.position", "cover.tilt_position"):
|
|
|
|
range_capability = get_capability(
|
|
|
|
capabilities, "Alexa.RangeController", range_instance
|
|
|
|
)
|
|
|
|
semantics = range_capability["semantics"]
|
|
|
|
assert semantics is not None
|
|
|
|
|
|
|
|
action_mappings = semantics["actionMappings"]
|
|
|
|
assert action_mappings is not None
|
|
|
|
if range_instance == "cover.position":
|
|
|
|
assert {
|
|
|
|
"@type": "ActionsToDirective",
|
|
|
|
"actions": ["Alexa.Actions.Lower"],
|
|
|
|
"directive": {"name": "SetRangeValue", "payload": {"rangeValue": 0}},
|
|
|
|
} in action_mappings
|
|
|
|
assert {
|
|
|
|
"@type": "ActionsToDirective",
|
|
|
|
"actions": ["Alexa.Actions.Raise"],
|
|
|
|
"directive": {"name": "SetRangeValue", "payload": {"rangeValue": 100}},
|
|
|
|
} in action_mappings
|
|
|
|
elif range_instance == "cover.position":
|
|
|
|
assert {
|
|
|
|
"@type": "ActionsToDirective",
|
|
|
|
"actions": ["Alexa.Actions.Close"],
|
|
|
|
"directive": {"name": "SetRangeValue", "payload": {"rangeValue": 0}},
|
|
|
|
} in action_mappings
|
|
|
|
assert {
|
|
|
|
"@type": "ActionsToDirective",
|
|
|
|
"actions": ["Alexa.Actions.Open"],
|
|
|
|
"directive": {"name": "SetRangeValue", "payload": {"rangeValue": 100}},
|
|
|
|
} in action_mappings
|
|
|
|
|
|
|
|
state_mappings = semantics["stateMappings"]
|
|
|
|
assert state_mappings is not None
|
|
|
|
assert {
|
|
|
|
"@type": "StatesToValue",
|
|
|
|
"states": ["Alexa.States.Closed"],
|
|
|
|
"value": 0,
|
|
|
|
} in state_mappings
|
|
|
|
assert {
|
|
|
|
"@type": "StatesToRange",
|
|
|
|
"states": ["Alexa.States.Open"],
|
|
|
|
"range": {"minimumValue": 1, "maximumValue": 100},
|
|
|
|
} in state_mappings
|
2019-12-23 14:17:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_input_number(hass):
|
|
|
|
"""Test input_number discovery."""
|
|
|
|
device = (
|
|
|
|
"input_number.test_slider",
|
|
|
|
30,
|
|
|
|
{
|
|
|
|
"initial": 30,
|
|
|
|
"min": -20,
|
|
|
|
"max": 35,
|
|
|
|
"step": 1,
|
|
|
|
"mode": "slider",
|
|
|
|
"friendly_name": "Test Slider",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "input_number#test_slider"
|
|
|
|
assert appliance["displayCategories"][0] == "OTHER"
|
|
|
|
assert appliance["friendlyName"] == "Test Slider"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa.RangeController", "Alexa.EndpointHealth", "Alexa"
|
|
|
|
)
|
|
|
|
|
|
|
|
range_capability = get_capability(
|
|
|
|
capabilities, "Alexa.RangeController", "input_number.value"
|
|
|
|
)
|
|
|
|
|
|
|
|
capability_resources = range_capability["capabilityResources"]
|
|
|
|
assert capability_resources is not None
|
|
|
|
assert {
|
|
|
|
"@type": "text",
|
|
|
|
"value": {"text": "Value", "locale": "en-US"},
|
|
|
|
} in capability_resources["friendlyNames"]
|
|
|
|
|
|
|
|
configuration = range_capability["configuration"]
|
|
|
|
assert configuration is not None
|
|
|
|
|
|
|
|
supported_range = configuration["supportedRange"]
|
|
|
|
assert supported_range["minimumValue"] == -20
|
|
|
|
assert supported_range["maximumValue"] == 35
|
|
|
|
assert supported_range["precision"] == 1
|
|
|
|
|
|
|
|
presets = configuration["presets"]
|
|
|
|
assert {
|
|
|
|
"rangeValue": 35,
|
|
|
|
"presetResources": {
|
|
|
|
"friendlyNames": [
|
|
|
|
{"@type": "asset", "value": {"assetId": "Alexa.Value.Maximum"}}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
} in presets
|
|
|
|
|
|
|
|
assert {
|
|
|
|
"rangeValue": -20,
|
|
|
|
"presetResources": {
|
|
|
|
"friendlyNames": [
|
|
|
|
{"@type": "asset", "value": {"assetId": "Alexa.Value.Minimum"}}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
} in presets
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"SetRangeValue",
|
|
|
|
"input_number#test_slider",
|
|
|
|
"input_number.set_value",
|
|
|
|
hass,
|
|
|
|
payload={"rangeValue": "10"},
|
|
|
|
instance="input_number.value",
|
|
|
|
)
|
|
|
|
assert call.data["value"] == 10
|
|
|
|
|
|
|
|
await assert_range_changes(
|
|
|
|
hass,
|
|
|
|
[(25, "-5"), (35, "5"), (-20, "-100"), (35, "100")],
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"AdjustRangeValue",
|
|
|
|
"input_number#test_slider",
|
|
|
|
False,
|
|
|
|
"input_number.set_value",
|
|
|
|
"value",
|
|
|
|
instance="input_number.value",
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_input_number_float(hass):
|
|
|
|
"""Test input_number discovery."""
|
|
|
|
device = (
|
|
|
|
"input_number.test_slider_float",
|
|
|
|
0.5,
|
|
|
|
{
|
|
|
|
"initial": 0.5,
|
|
|
|
"min": 0,
|
|
|
|
"max": 1,
|
|
|
|
"step": 0.01,
|
|
|
|
"mode": "slider",
|
|
|
|
"friendly_name": "Test Slider Float",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "input_number#test_slider_float"
|
|
|
|
assert appliance["displayCategories"][0] == "OTHER"
|
|
|
|
assert appliance["friendlyName"] == "Test Slider Float"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa.RangeController", "Alexa.EndpointHealth", "Alexa"
|
|
|
|
)
|
|
|
|
|
|
|
|
range_capability = get_capability(
|
|
|
|
capabilities, "Alexa.RangeController", "input_number.value"
|
|
|
|
)
|
|
|
|
|
|
|
|
capability_resources = range_capability["capabilityResources"]
|
|
|
|
assert capability_resources is not None
|
|
|
|
assert {
|
|
|
|
"@type": "text",
|
|
|
|
"value": {"text": "Value", "locale": "en-US"},
|
|
|
|
} in capability_resources["friendlyNames"]
|
|
|
|
|
|
|
|
configuration = range_capability["configuration"]
|
|
|
|
assert configuration is not None
|
|
|
|
|
|
|
|
supported_range = configuration["supportedRange"]
|
|
|
|
assert supported_range["minimumValue"] == 0
|
|
|
|
assert supported_range["maximumValue"] == 1
|
|
|
|
assert supported_range["precision"] == 0.01
|
|
|
|
|
|
|
|
presets = configuration["presets"]
|
|
|
|
assert {
|
|
|
|
"rangeValue": 1,
|
|
|
|
"presetResources": {
|
|
|
|
"friendlyNames": [
|
|
|
|
{"@type": "asset", "value": {"assetId": "Alexa.Value.Maximum"}}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
} in presets
|
|
|
|
|
|
|
|
assert {
|
|
|
|
"rangeValue": 0,
|
|
|
|
"presetResources": {
|
|
|
|
"friendlyNames": [
|
|
|
|
{"@type": "asset", "value": {"assetId": "Alexa.Value.Minimum"}}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
} in presets
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"SetRangeValue",
|
|
|
|
"input_number#test_slider_float",
|
|
|
|
"input_number.set_value",
|
|
|
|
hass,
|
|
|
|
payload={"rangeValue": "0.333"},
|
|
|
|
instance="input_number.value",
|
|
|
|
)
|
|
|
|
assert call.data["value"] == 0.333
|
|
|
|
|
|
|
|
await assert_range_changes(
|
|
|
|
hass,
|
|
|
|
[(0.4, "-0.1"), (0.6, "0.1"), (0, "-100"), (1, "100"), (0.51, "0.01")],
|
|
|
|
"Alexa.RangeController",
|
|
|
|
"AdjustRangeValue",
|
|
|
|
"input_number#test_slider_float",
|
|
|
|
False,
|
|
|
|
"input_number.set_value",
|
|
|
|
"value",
|
|
|
|
instance="input_number.value",
|
|
|
|
)
|
2019-12-24 22:06:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_media_player_eq_modes(hass):
|
|
|
|
"""Test media player discovery with sound mode list."""
|
|
|
|
device = (
|
|
|
|
"media_player.test",
|
|
|
|
"on",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test media player",
|
|
|
|
"supported_features": SUPPORT_SELECT_SOUND_MODE,
|
|
|
|
"sound_mode": "tv",
|
|
|
|
"sound_mode_list": ["movie", "music", "night", "sport", "tv", "rocknroll"],
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "media_player#test"
|
|
|
|
assert appliance["friendlyName"] == "Test media player"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance,
|
|
|
|
"Alexa",
|
|
|
|
"Alexa.EqualizerController",
|
|
|
|
"Alexa.PowerController",
|
|
|
|
"Alexa.EndpointHealth",
|
|
|
|
)
|
|
|
|
|
|
|
|
eq_capability = get_capability(capabilities, "Alexa.EqualizerController")
|
|
|
|
assert eq_capability is not None
|
|
|
|
assert "modes" in eq_capability["configurations"]
|
|
|
|
|
|
|
|
eq_modes = eq_capability["configurations"]["modes"]
|
|
|
|
assert {"name": "rocknroll"} not in eq_modes["supported"]
|
|
|
|
assert {"name": "ROCKNROLL"} not in eq_modes["supported"]
|
|
|
|
|
|
|
|
for mode in ("MOVIE", "MUSIC", "NIGHT", "SPORT", "TV"):
|
|
|
|
assert {"name": mode} in eq_modes["supported"]
|
|
|
|
|
|
|
|
call, _ = await assert_request_calls_service(
|
|
|
|
"Alexa.EqualizerController",
|
|
|
|
"SetMode",
|
|
|
|
"media_player#test",
|
|
|
|
"media_player.select_sound_mode",
|
|
|
|
hass,
|
|
|
|
payload={"mode": mode},
|
|
|
|
)
|
|
|
|
assert call.data["sound_mode"] == mode.lower()
|
|
|
|
|
|
|
|
|
|
|
|
async def test_media_player_sound_mode_list_none(hass):
|
|
|
|
"""Test EqualizerController bands directive not supported."""
|
|
|
|
device = (
|
|
|
|
"media_player.test",
|
|
|
|
"on",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test media player",
|
|
|
|
"supported_features": SUPPORT_SELECT_SOUND_MODE,
|
|
|
|
"sound_mode": "unknown",
|
|
|
|
"sound_mode_list": None,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
assert appliance["endpointId"] == "media_player#test"
|
|
|
|
assert appliance["friendlyName"] == "Test media player"
|
|
|
|
|
|
|
|
|
|
|
|
async def test_media_player_eq_bands_not_supported(hass):
|
|
|
|
"""Test EqualizerController bands directive not supported."""
|
|
|
|
device = (
|
|
|
|
"media_player.test_bands",
|
|
|
|
"on",
|
|
|
|
{
|
|
|
|
"friendly_name": "Test media player",
|
|
|
|
"supported_features": SUPPORT_SELECT_SOUND_MODE,
|
|
|
|
"sound_mode": "tv",
|
|
|
|
"sound_mode_list": ["movie", "music", "night", "sport", "tv", "rocknroll"],
|
|
|
|
},
|
|
|
|
)
|
|
|
|
await discovery_test(device, hass)
|
|
|
|
|
|
|
|
context = Context()
|
|
|
|
|
|
|
|
# Test for SetBands Error
|
|
|
|
request = get_new_request(
|
|
|
|
"Alexa.EqualizerController", "SetBands", "media_player#test_bands"
|
|
|
|
)
|
|
|
|
request["directive"]["payload"] = {"bands": [{"name": "BASS", "value": -2}]}
|
|
|
|
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request, context)
|
|
|
|
|
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
|
|
|
assert msg["header"]["name"] == "ErrorResponse"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa"
|
|
|
|
assert msg["payload"]["type"] == "INVALID_DIRECTIVE"
|
|
|
|
|
|
|
|
# Test for AdjustBands Error
|
|
|
|
request = get_new_request(
|
|
|
|
"Alexa.EqualizerController", "AdjustBands", "media_player#test_bands"
|
|
|
|
)
|
|
|
|
request["directive"]["payload"] = {
|
|
|
|
"bands": [{"name": "BASS", "levelDelta": 3, "levelDirection": "UP"}]
|
|
|
|
}
|
|
|
|
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request, context)
|
|
|
|
|
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
|
|
|
assert msg["header"]["name"] == "ErrorResponse"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa"
|
|
|
|
assert msg["payload"]["type"] == "INVALID_DIRECTIVE"
|
|
|
|
|
|
|
|
# Test for ResetBands Error
|
|
|
|
request = get_new_request(
|
|
|
|
"Alexa.EqualizerController", "ResetBands", "media_player#test_bands"
|
|
|
|
)
|
|
|
|
request["directive"]["payload"] = {
|
|
|
|
"bands": [{"name": "BASS", "levelDelta": 3, "levelDirection": "UP"}]
|
|
|
|
}
|
|
|
|
msg = await smart_home.async_handle_message(hass, DEFAULT_CONFIG, request, context)
|
|
|
|
|
|
|
|
assert "event" in msg
|
|
|
|
msg = msg["event"]
|
|
|
|
assert msg["header"]["name"] == "ErrorResponse"
|
|
|
|
assert msg["header"]["namespace"] == "Alexa"
|
|
|
|
assert msg["payload"]["type"] == "INVALID_DIRECTIVE"
|
2020-01-10 22:11:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_timer_hold(hass):
|
|
|
|
"""Test timer hold."""
|
|
|
|
device = (
|
|
|
|
"timer.laundry",
|
|
|
|
"active",
|
|
|
|
{"friendly_name": "Laundry", "duration": "00:01:00", "remaining": "00:50:00"},
|
|
|
|
)
|
|
|
|
appliance = await discovery_test(device, hass)
|
|
|
|
|
|
|
|
assert appliance["endpointId"] == "timer#laundry"
|
|
|
|
assert appliance["displayCategories"][0] == "OTHER"
|
|
|
|
assert appliance["friendlyName"] == "Laundry"
|
|
|
|
|
|
|
|
capabilities = assert_endpoint_capabilities(
|
|
|
|
appliance, "Alexa", "Alexa.TimeHoldController"
|
|
|
|
)
|
|
|
|
|
|
|
|
time_hold_capability = get_capability(capabilities, "Alexa.TimeHoldController")
|
|
|
|
assert time_hold_capability is not None
|
|
|
|
configuration = time_hold_capability["configuration"]
|
|
|
|
assert configuration["allowRemoteResume"] is True
|
|
|
|
|
|
|
|
await assert_request_calls_service(
|
|
|
|
"Alexa.TimeHoldController", "Hold", "timer#laundry", "timer.pause", hass
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
async def test_timer_resume(hass):
|
|
|
|
"""Test timer resume."""
|
|
|
|
device = (
|
|
|
|
"timer.laundry",
|
|
|
|
"paused",
|
|
|
|
{"friendly_name": "Laundry", "duration": "00:01:00", "remaining": "00:50:00"},
|
|
|
|
)
|
|
|
|
await discovery_test(device, hass)
|
|
|
|
|
|
|
|
await assert_request_calls_service(
|
|
|
|
"Alexa.TimeHoldController", "Resume", "timer#laundry", "timer.start", hass
|
|
|
|
)
|