Allow separate URL for REST switch state (#39557)

This commit is contained in:
jjlawren 2020-09-04 09:58:40 -05:00 committed by GitHub
parent ebc31c0f08
commit f01a0f9151
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 73 additions and 22 deletions

View file

@ -30,6 +30,7 @@ _LOGGER = logging.getLogger(__name__)
CONF_BODY_OFF = "body_off"
CONF_BODY_ON = "body_on"
CONF_IS_ON_TEMPLATE = "is_on_template"
CONF_STATE_RESOURCE = "state_resource"
DEFAULT_METHOD = "post"
DEFAULT_BODY_OFF = "OFF"
@ -43,6 +44,7 @@ SUPPORT_REST_METHODS = ["post", "put"]
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_RESOURCE): cv.url,
vol.Optional(CONF_STATE_RESOURCE): cv.url,
vol.Optional(CONF_HEADERS): {cv.string: cv.string},
vol.Optional(CONF_BODY_OFF, default=DEFAULT_BODY_OFF): cv.template,
vol.Optional(CONF_BODY_ON, default=DEFAULT_BODY_ON): cv.template,
@ -73,6 +75,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
resource = config.get(CONF_RESOURCE)
state_resource = config.get(CONF_STATE_RESOURCE) or resource
verify_ssl = config.get(CONF_VERIFY_SSL)
auth = None
@ -91,6 +94,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
switch = RestSwitch(
name,
resource,
state_resource,
method,
headers,
auth,
@ -122,6 +126,7 @@ class RestSwitch(SwitchEntity):
self,
name,
resource,
state_resource,
method,
headers,
auth,
@ -135,6 +140,7 @@ class RestSwitch(SwitchEntity):
self._state = None
self._name = name
self._resource = resource
self._state_resource = state_resource
self._method = method
self._headers = headers
self._auth = auth
@ -213,7 +219,7 @@ class RestSwitch(SwitchEntity):
with async_timeout.timeout(self._timeout):
req = await websession.get(
self._resource, auth=self._auth, headers=self._headers
self._state_resource, auth=self._auth, headers=self._headers
)
text = await req.text()

View file

@ -4,7 +4,17 @@ import asyncio
import aiohttp
import homeassistant.components.rest.switch as rest
from homeassistant.const import HTTP_INTERNAL_SERVER_ERROR
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.const import (
CONF_HEADERS,
CONF_NAME,
CONF_PLATFORM,
CONF_RESOURCE,
CONTENT_TYPE_JSON,
HTTP_INTERNAL_SERVER_ERROR,
HTTP_NOT_FOUND,
HTTP_OK,
)
from homeassistant.helpers.template import Template
from homeassistant.setup import setup_component
@ -25,7 +35,7 @@ class TestRestSwitchSetup:
def test_setup_missing_config(self):
"""Test setup with configuration missing required entries."""
assert not asyncio.run_coroutine_threadsafe(
rest.async_setup_platform(self.hass, {"platform": "rest"}, None),
rest.async_setup_platform(self.hass, {CONF_PLATFORM: rest.DOMAIN}, None),
self.hass.loop,
).result()
@ -33,7 +43,9 @@ class TestRestSwitchSetup:
"""Test setup with resource missing schema."""
assert not asyncio.run_coroutine_threadsafe(
rest.async_setup_platform(
self.hass, {"platform": "rest", "resource": "localhost"}, None
self.hass,
{CONF_PLATFORM: rest.DOMAIN, CONF_RESOURCE: "localhost"},
None,
),
self.hass.loop,
).result()
@ -43,7 +55,9 @@ class TestRestSwitchSetup:
aioclient_mock.get("http://localhost", exc=aiohttp.ClientError)
assert not asyncio.run_coroutine_threadsafe(
rest.async_setup_platform(
self.hass, {"platform": "rest", "resource": "http://localhost"}, None
self.hass,
{CONF_PLATFORM: rest.DOMAIN, CONF_RESOURCE: "http://localhost"},
None,
),
self.hass.loop,
).result()
@ -53,41 +67,70 @@ class TestRestSwitchSetup:
aioclient_mock.get("http://localhost", exc=asyncio.TimeoutError())
assert not asyncio.run_coroutine_threadsafe(
rest.async_setup_platform(
self.hass, {"platform": "rest", "resource": "http://localhost"}, None
self.hass,
{CONF_PLATFORM: rest.DOMAIN, CONF_RESOURCE: "http://localhost"},
None,
),
self.hass.loop,
).result()
def test_setup_minimum(self, aioclient_mock):
"""Test setup with minimum configuration."""
aioclient_mock.get("http://localhost", status=200)
with assert_setup_component(1, "switch"):
aioclient_mock.get("http://localhost", status=HTTP_OK)
with assert_setup_component(1, SWITCH_DOMAIN):
assert setup_component(
self.hass,
"switch",
{"switch": {"platform": "rest", "resource": "http://localhost"}},
SWITCH_DOMAIN,
{
SWITCH_DOMAIN: {
CONF_PLATFORM: rest.DOMAIN,
CONF_RESOURCE: "http://localhost",
}
},
)
assert aioclient_mock.call_count == 1
def test_setup(self, aioclient_mock):
"""Test setup with valid configuration."""
aioclient_mock.get("http://localhost", status=200)
aioclient_mock.get("http://localhost", status=HTTP_OK)
assert setup_component(
self.hass,
"switch",
SWITCH_DOMAIN,
{
"switch": {
"platform": "rest",
"name": "foo",
"resource": "http://localhost",
"headers": {"Content-type": "application/json"},
"body_on": "custom on text",
"body_off": "custom off text",
SWITCH_DOMAIN: {
CONF_PLATFORM: rest.DOMAIN,
CONF_NAME: "foo",
CONF_RESOURCE: "http://localhost",
CONF_HEADERS: {"Content-type": CONTENT_TYPE_JSON},
rest.CONF_BODY_ON: "custom on text",
rest.CONF_BODY_OFF: "custom off text",
}
},
)
assert aioclient_mock.call_count == 1
assert_setup_component(1, "switch")
assert_setup_component(1, SWITCH_DOMAIN)
def test_setup_with_state_resource(self, aioclient_mock):
"""Test setup with valid configuration."""
aioclient_mock.get("http://localhost", status=HTTP_NOT_FOUND)
aioclient_mock.get("http://localhost/state", status=HTTP_OK)
assert setup_component(
self.hass,
SWITCH_DOMAIN,
{
SWITCH_DOMAIN: {
CONF_PLATFORM: rest.DOMAIN,
CONF_NAME: "foo",
CONF_RESOURCE: "http://localhost",
rest.CONF_STATE_RESOURCE: "http://localhost/state",
CONF_HEADERS: {"Content-type": "application/json"},
rest.CONF_BODY_ON: "custom on text",
rest.CONF_BODY_OFF: "custom off text",
}
},
)
assert aioclient_mock.call_count == 1
assert_setup_component(1, SWITCH_DOMAIN)
class TestRestSwitch:
@ -99,6 +142,7 @@ class TestRestSwitch:
self.name = "foo"
self.method = "post"
self.resource = "http://localhost/"
self.state_resource = self.resource
self.headers = {"Content-type": "application/json"}
self.auth = None
self.body_on = Template("on", self.hass)
@ -106,6 +150,7 @@ class TestRestSwitch:
self.switch = rest.RestSwitch(
self.name,
self.resource,
self.state_resource,
self.method,
self.headers,
self.auth,
@ -131,7 +176,7 @@ class TestRestSwitch:
def test_turn_on_success(self, aioclient_mock):
"""Test turn_on."""
aioclient_mock.post(self.resource, status=200)
aioclient_mock.post(self.resource, status=HTTP_OK)
asyncio.run_coroutine_threadsafe(
self.switch.async_turn_on(), self.hass.loop
).result()
@ -160,7 +205,7 @@ class TestRestSwitch:
def test_turn_off_success(self, aioclient_mock):
"""Test turn_off."""
aioclient_mock.post(self.resource, status=200)
aioclient_mock.post(self.resource, status=HTTP_OK)
asyncio.run_coroutine_threadsafe(
self.switch.async_turn_off(), self.hass.loop
).result()