Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 54 additions & 42 deletions homeassistant/components/switch/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
from homeassistant.const import (
CONF_NAME, CONF_RESOURCE, CONF_TIMEOUT, CONF_METHOD)
CONF_NAME, CONF_RESOURCE, CONF_TIMEOUT, CONF_METHOD, CONF_USERNAME,
CONF_PASSWORD)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.template import Template
Expand Down Expand Up @@ -41,6 +42,8 @@
vol.All(vol.Lower, vol.In(SUPPORT_REST_METHODS)),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Inclusive(CONF_USERNAME, 'authentication'): cv.string,
vol.Inclusive(CONF_PASSWORD, 'authentication'): cv.string,
})


Expand All @@ -53,8 +56,13 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
is_on_template = config.get(CONF_IS_ON_TEMPLATE)
method = config.get(CONF_METHOD)
name = config.get(CONF_NAME)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
resource = config.get(CONF_RESOURCE)
websession = async_get_clientsession(hass)

auth = None
if username:
auth = aiohttp.BasicAuth(username, password=password)

if is_on_template is not None:
is_on_template.hass = hass
Expand All @@ -65,37 +73,32 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
timeout = config.get(CONF_TIMEOUT)

try:
with async_timeout.timeout(timeout, loop=hass.loop):
req = yield from websession.get(resource)
switch = RestSwitch(name, resource, method, auth, body_on, body_off,
is_on_template, timeout)

req = yield from switch.get_device_state(hass)
if req.status >= 400:
_LOGGER.error("Got non-ok response from resource: %s", req.status)
return False

else:
async_add_devices([switch])
except (TypeError, ValueError):
_LOGGER.error("Missing resource or schema in configuration. "
"Add http:// or https:// to your URL")
return False
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("No route to resource/endpoint: %s", resource)
return False

async_add_devices(
[RestSwitch(hass, name, resource, method, body_on, body_off,
is_on_template, timeout)])


class RestSwitch(SwitchDevice):
"""Representation of a switch that can be toggled using REST."""

def __init__(self, hass, name, resource, method, body_on, body_off,
def __init__(self, name, resource, method, auth, body_on, body_off,
is_on_template, timeout):
"""Initialize the REST switch."""
self._state = None
self.hass = hass
self._name = name
self._resource = resource
self._method = method
self._auth = auth
self._body_on = body_on
self._body_off = body_off
self._is_on_template = is_on_template
Expand All @@ -115,54 +118,61 @@ def is_on(self):
def async_turn_on(self, **kwargs):
"""Turn the device on."""
body_on_t = self._body_on.async_render()
websession = async_get_clientsession(self.hass)

try:
with async_timeout.timeout(self._timeout, loop=self.hass.loop):
request = yield from getattr(websession, self._method)(
self._resource, data=bytes(body_on_t, 'utf-8'))
req = yield from self.set_device_state(body_on_t)

if req.status == 200:
self._state = True
else:
_LOGGER.error(
"Can't turn on %s. Is resource/endpoint offline?",
self._resource)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Error while turn on %s", self._resource)
return

if request.status == 200:
self._state = True
else:
_LOGGER.error("Can't turn on %s. Is resource/endpoint offline?",
self._resource)

@asyncio.coroutine
def async_turn_off(self, **kwargs):
"""Turn the device off."""
body_off_t = self._body_off.async_render()
websession = async_get_clientsession(self.hass)

try:
with async_timeout.timeout(self._timeout, loop=self.hass.loop):
request = yield from getattr(websession, self._method)(
self._resource, data=bytes(body_off_t, 'utf-8'))
req = yield from self.set_device_state(body_off_t)
if req.status == 200:
self._state = False
else:
_LOGGER.error(
"Can't turn off %s. Is resource/endpoint offline?",
self._resource)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Error while turn off %s", self._resource)
return

if request.status == 200:
self._state = False
else:
_LOGGER.error("Can't turn off %s. Is resource/endpoint offline?",
self._resource)

@asyncio.coroutine
def async_update(self):
"""Get the latest data from REST API and update the state."""
def set_device_state(self, body):
"""Send a state update to the device."""
websession = async_get_clientsession(self.hass)

with async_timeout.timeout(self._timeout, loop=self.hass.loop):
req = yield from getattr(websession, self._method)(
self._resource, auth=self._auth, data=bytes(body, 'utf-8'))
return req

@asyncio.coroutine
def async_update(self):
"""Get the current state, catching errors."""
try:
with async_timeout.timeout(self._timeout, loop=self.hass.loop):
request = yield from websession.get(self._resource)
text = yield from request.text()
yield from self.get_device_state(self.hass)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.exception("Error while fetch data.")
return

@asyncio.coroutine
def get_device_state(self, hass):
"""Get the latest data from REST API and update the state."""
websession = async_get_clientsession(hass)

with async_timeout.timeout(self._timeout, loop=hass.loop):
req = yield from websession.get(self._resource, auth=self._auth)
text = yield from req.text()

if self._is_on_template is not None:
text = self._is_on_template.async_render_with_possible_json_value(
Expand All @@ -181,3 +191,5 @@ def async_update(self):
self._state = False
else:
self._state = None

return req
4 changes: 2 additions & 2 deletions homeassistant/const.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# coding: utf-8
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 53
PATCH_VERSION = '0'
MINOR_VERSION = 54
PATCH_VERSION = '0.dev0'
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
REQUIRED_PYTHON_VER = (3, 4, 2)
Expand Down
4 changes: 3 additions & 1 deletion tests/components/switch/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,13 @@ def setup_method(self):
self.name = 'foo'
self.method = 'post'
self.resource = 'http://localhost/'
self.auth = None
self.body_on = Template('on', self.hass)
self.body_off = Template('off', self.hass)
self.switch = rest.RestSwitch(
self.hass, self.name, self.resource, self.method, self.body_on,
self.name, self.resource, self.method, self.auth, self.body_on,
self.body_off, None, 10)
self.switch.hass = self.hass

def teardown_method(self):
"""Stop everything that was started."""
Expand Down