diff --git a/.coveragerc b/.coveragerc index f1631b4e99f..3ee6d3cb81a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -168,6 +168,7 @@ omit = homeassistant/components/device_tracker/swisscom.py homeassistant/components/device_tracker/thomson.py homeassistant/components/device_tracker/tomato.py + homeassistant/components/device_tracker/tado.py homeassistant/components/device_tracker/tplink.py homeassistant/components/device_tracker/trackr.py homeassistant/components/device_tracker/ubus.py diff --git a/homeassistant/components/device_tracker/tado.py b/homeassistant/components/device_tracker/tado.py new file mode 100644 index 00000000000..5cb1f8fcbd2 --- /dev/null +++ b/homeassistant/components/device_tracker/tado.py @@ -0,0 +1,130 @@ +""" +Support for Tado Smart Thermostat. + +Device tracker platform that supports presence detection. +The detection is based on geofencing enabled devices used with Tado. +""" +import logging +from datetime import timedelta +from collections import namedtuple + +import asyncio +import aiohttp +import async_timeout + +import voluptuous as vol + +from homeassistant.const import CONF_USERNAME, CONF_PASSWORD +import homeassistant.helpers.config_validation as cv +from homeassistant.util import Throttle +from homeassistant.components.device_tracker import \ + DOMAIN, PLATFORM_SCHEMA, DeviceScanner +from homeassistant.helpers.aiohttp_client import async_create_clientsession + +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=30) + +_LOGGER = logging.getLogger(__name__) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_PASSWORD): cv.string, + vol.Required(CONF_USERNAME): cv.string +}) + + +def get_scanner(hass, config): + """Return a Tado scanner.""" + scanner = TadoDeviceScanner(hass, config[DOMAIN]) + + return scanner if scanner.success_init else None + + +Device = namedtuple("Device", ["mac", "name"]) + + +class TadoDeviceScanner(DeviceScanner): + """This class gets geofenced devices from Tado.""" + + def __init__(self, hass, config): + """Initialize the scanner.""" + self.last_results = [] + + self.username = config[CONF_USERNAME] + self.password = config[CONF_PASSWORD] + self.tadoapiurl = 'https://my.tado.com/api/v2/me' \ + '?username={}&password={}' + + self.websession = async_create_clientsession( + hass, cookie_jar=aiohttp.CookieJar(unsafe=True, loop=hass.loop)) + + self.success_init = self._update_info() + _LOGGER.info("Tado scanner initialized") + + @asyncio.coroutine + def async_scan_devices(self): + """Scan for devices and return a list containing found device ids.""" + yield from self._update_info() + + return [device.mac for device in self.last_results] + + @asyncio.coroutine + def async_get_device_name(self, mac): + """Return the name of the given device or None if we don't know.""" + filter_named = [device.name for device in self.last_results + if device.mac == mac] + + if filter_named: + return filter_named[0] + else: + return None + + @Throttle(MIN_TIME_BETWEEN_SCANS) + def _update_info(self): + """ + Query Tado for device marked as at home. + + Returns boolean if scanning successful. + """ + _LOGGER.debug("Requesting Tado") + + last_results = [] + + response = None + tadojson = None + try: + # get first token + with async_timeout.timeout(10, loop=self.hass.loop): + url = self.tadoapiurl.format(self.username, self.password) + response = yield from self.websession.get( + url + ) + + # error on Tado webservice + if response.status != 200: + _LOGGER.warning( + "Error %d on %s.", response.status, self.tadoapiurl) + self.token = None + return + + tadojson = yield from response.json() + + except (asyncio.TimeoutError, aiohttp.errors.ClientError): + _LOGGER.error("Can not load Tado data") + return False + + finally: + if response is not None: + yield from response.release() + + # Find devices that have geofencing enabled, and are currently at home + for mobiledevice in tadojson['mobileDevices']: + if 'location' in mobiledevice: + if mobiledevice['location']['atHome']: + deviceid = mobiledevice['id'] + devicename = mobiledevice['name'] + last_results.append(Device(deviceid, devicename)) + + self.last_results = last_results + + _LOGGER.info("Tado presence query successful") + return True