core/homeassistant/components/hook/switch.py

133 lines
4.6 KiB
Python

"""Support Hook, available at hooksmarthome.com."""
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, CONF_TOKEN
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.Exclusive(
CONF_PASSWORD,
"hook_secret",
msg="hook: provide " + "username/password OR token",
): cv.string,
vol.Exclusive(
CONF_TOKEN,
"hook_secret",
msg="hook: provide " + "username/password OR token",
): cv.string,
vol.Inclusive(CONF_USERNAME, "hook_auth"): cv.string,
vol.Inclusive(CONF_PASSWORD, "hook_auth"): cv.string,
}
)
async def async_setup_platform(hass, config, async_add_entities, 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)
token = config.get(CONF_TOKEN)
websession = async_get_clientsession(hass)
# If password is set in config, prefer it over token
if username is not None and password is not None:
try:
with async_timeout.timeout(TIMEOUT):
response = await websession.post(
"{}{}".format(HOOK_ENDPOINT, "user/login"),
data={"username": username, "password": password},
)
# The Hook API returns JSON but calls it 'text/html'. Setting
# content_type=None disables aiohttp's content-type validation.
data = await response.json(content_type=None)
except (asyncio.TimeoutError, aiohttp.ClientError) as error:
_LOGGER.error("Failed authentication API call: %s", error)
return False
try:
token = data["data"]["token"]
except KeyError:
_LOGGER.error("No token. Check username and password")
return False
try:
with async_timeout.timeout(TIMEOUT):
response = await websession.get(
"{}{}".format(HOOK_ENDPOINT, "device"), params={"token": token}
)
data = await response.json(content_type=None)
except (asyncio.TimeoutError, aiohttp.ClientError) as error:
_LOGGER.error("Failed getting devices: %s", error)
return False
async_add_entities(
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
async def _send(self, url):
"""Send the url to the Hook API."""
try:
_LOGGER.debug("Sending: %s", url)
websession = async_get_clientsession(self.hass)
with async_timeout.timeout(TIMEOUT):
response = await websession.get(url, params={"token": self._token})
data = await response.json(content_type=None)
except (asyncio.TimeoutError, aiohttp.ClientError) as error:
_LOGGER.error("Failed setting state: %s", error)
return False
_LOGGER.debug("Got: %s", data)
return data["return_value"] == "1"
async def async_turn_on(self, **kwargs):
"""Turn the device on asynchronously."""
_LOGGER.debug("Turning on: %s", self._name)
url = "{}{}{}{}".format(HOOK_ENDPOINT, "device/trigger/", self._id, "/On")
success = await self._send(url)
self._state = success
async def async_turn_off(self, **kwargs):
"""Turn the device off asynchronously."""
_LOGGER.debug("Turning off: %s", self._name)
url = "{}{}{}{}".format(HOOK_ENDPOINT, "device/trigger/", self._id, "/Off")
success = await self._send(url)
# If it wasn't successful, keep state as true
self._state = not success