142 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			5.0 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, 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,
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
@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)
 | 
						|
    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, loop=hass.loop):
 | 
						|
                response = yield from 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 = yield from 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, loop=hass.loop):
 | 
						|
            response = yield from websession.get(
 | 
						|
                '{}{}'.format(HOOK_ENDPOINT, 'device'),
 | 
						|
                params={"token": token})
 | 
						|
        data = yield from response.json(content_type=None)
 | 
						|
    except (asyncio.TimeoutError, aiohttp.ClientError) as error:
 | 
						|
        _LOGGER.error("Failed getting devices: %s", error)
 | 
						|
        return False
 | 
						|
 | 
						|
    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."""
 | 
						|
        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(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'
 | 
						|
 | 
						|
    @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
 |