core/homeassistant/components/switch/hook.py

153 lines
4.8 KiB
Python

"""
Support Hook, available at hooksmarthome.com.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.hook/
"""
import logging
import asyncio
import voluptuous as vol
import async_timeout
import aiohttp
from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
_LOGGER = logging.getLogger(__name__)
HOOK_ENDPOINT = 'https://api.gethook.io/v1/'
TIMEOUT = 10
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
})
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up Hook by getting the access token and list of actions."""
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
websession = async_get_clientsession(hass)
response = None
try:
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
response = yield from websession.post(
'{}{}'.format(HOOK_ENDPOINT, 'user/login'),
data={
'username': username,
'password': password})
data = yield from response.json()
except (asyncio.TimeoutError,
aiohttp.errors.ClientError,
aiohttp.errors.ClientDisconnectedError) as error:
_LOGGER.error("Failed authentication API call: %s", error)
return False
finally:
if response is not None:
yield from response.release()
try:
token = data['data']['token']
except KeyError:
_LOGGER.error("No token. Check username and password")
return False
response = None
try:
with async_timeout.timeout(TIMEOUT, loop=hass.loop):
response = yield from websession.get(
'{}{}'.format(HOOK_ENDPOINT, 'device'),
params={"token": data['data']['token']})
data = yield from response.json()
except (asyncio.TimeoutError,
aiohttp.errors.ClientError,
aiohttp.errors.ClientDisconnectedError) as error:
_LOGGER.error("Failed getting devices: %s", error)
return False
finally:
if response is not None:
yield from response.release()
yield from async_add_devices(
HookSmartHome(
hass,
token,
d['device_id'],
d['device_name'])
for lst in data['data']
for d in lst)
class HookSmartHome(SwitchDevice):
"""Representation of a Hook device, allowing on and off commands."""
def __init__(self, hass, token, device_id, device_name):
"""Initialize the switch."""
self.hass = hass
self._token = token
self._state = False
self._id = device_id
self._name = device_name
_LOGGER.debug(
"Creating Hook object: ID: %s Name: %s", self._id, self._name)
@property
def name(self):
"""Return the name of the switch."""
return self._name
@property
def is_on(self):
"""Return true if device is on."""
return self._state
@asyncio.coroutine
def _send(self, url):
"""Send the url to the Hook API."""
response = None
try:
_LOGGER.debug("Sending: %s", url)
websession = async_get_clientsession(self.hass)
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
response = yield from websession.get(
url, params={"token": self._token})
data = yield from response.json()
except (asyncio.TimeoutError,
aiohttp.errors.ClientError,
aiohttp.errors.ClientDisconnectedError) as error:
_LOGGER.error("Failed setting state: %s", error)
return False
finally:
if response is not None:
yield from response.release()
_LOGGER.debug("Got: %s", data)
return data['return_value'] == '1'
@asyncio.coroutine
def async_turn_on(self):
"""Turn the device on asynchronously."""
_LOGGER.debug("Turning on: %s", self._name)
url = '{}{}{}{}'.format(
HOOK_ENDPOINT, 'device/trigger/', self._id, '/On')
success = yield from self._send(url)
self._state = success
@asyncio.coroutine
def async_turn_off(self):
"""Turn the device off asynchronously."""
_LOGGER.debug("Turning off: %s", self._name)
url = '{}{}{}{}'.format(
HOOK_ENDPOINT, 'device/trigger/', self._id, '/Off')
success = yield from self._send(url)
# If it wasn't successful, keep state as true
self._state = not success