diff --git a/.coveragerc b/.coveragerc index ffe89740432..353aeaf5684 100644 --- a/.coveragerc +++ b/.coveragerc @@ -538,6 +538,7 @@ omit = homeassistant/components/slack/notify.py homeassistant/components/sma/sensor.py homeassistant/components/smappee/* + homeassistant/components/smarthab/* homeassistant/components/smtp/notify.py homeassistant/components/snapcast/media_player.py homeassistant/components/snmp/* diff --git a/CODEOWNERS b/CODEOWNERS index 90fb72378bc..cc4138b362d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -205,6 +205,7 @@ homeassistant/components/shiftr/* @fabaff homeassistant/components/shodan/* @fabaff homeassistant/components/simplisafe/* @bachya homeassistant/components/sma/* @kellerza +homeassistant/components/smarthab/* @outadoc homeassistant/components/smartthings/* @andrewsayre homeassistant/components/smtp/* @fabaff homeassistant/components/sonos/* @amelchio diff --git a/homeassistant/components/smarthab/__init__.py b/homeassistant/components/smarthab/__init__.py new file mode 100644 index 00000000000..af592b60a91 --- /dev/null +++ b/homeassistant/components/smarthab/__init__.py @@ -0,0 +1,61 @@ +""" +Support for SmartHab device integration. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/smarthab/ +""" +import logging + +import voluptuous as vol + +from homeassistant.helpers.discovery import load_platform +from homeassistant.const import CONF_EMAIL, CONF_PASSWORD +import homeassistant.helpers.config_validation as cv + +DOMAIN = 'smarthab' +DATA_HUB = 'hub' + +_LOGGER = logging.getLogger(__name__) + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_EMAIL): cv.string, + vol.Required(CONF_PASSWORD): cv.string + }), +}, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config) -> bool: + """Set up the SmartHab platform.""" + import pysmarthab + + sh_conf = config.get(DOMAIN) + + # Assign configuration variables + username = sh_conf[CONF_EMAIL] + password = sh_conf[CONF_PASSWORD] + + # Setup connection with SmartHab API + hub = pysmarthab.SmartHab() + + try: + hub.login(username, password) + except pysmarthab.RequestFailedException as ex: + _LOGGER.error("Error while trying to reach SmartHab API.") + _LOGGER.debug(ex, exc_info=True) + return False + + # Verify that passed in configuration works + if not hub.is_logged_in(): + _LOGGER.error("Could not authenticate with SmartHab API") + return False + + # Pass hub object to child platforms + hass.data[DOMAIN] = { + DATA_HUB: hub + } + + load_platform(hass, 'light', DOMAIN, None, config) + load_platform(hass, 'cover', DOMAIN, None, config) + + return True diff --git a/homeassistant/components/smarthab/cover.py b/homeassistant/components/smarthab/cover.py new file mode 100644 index 00000000000..0b8cc0604e7 --- /dev/null +++ b/homeassistant/components/smarthab/cover.py @@ -0,0 +1,100 @@ +""" +Support for SmartHab device integration. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/smarthab/ +""" +import logging +from datetime import timedelta +from requests.exceptions import Timeout + +from homeassistant.components.cover import ( + CoverDevice, SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_SET_POSITION, + ATTR_POSITION +) +from . import DOMAIN, DATA_HUB + +_LOGGER = logging.getLogger(__name__) + +SCAN_INTERVAL = timedelta(seconds=60) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the SmartHab roller shutters platform.""" + import pysmarthab + + hub = hass.data[DOMAIN][DATA_HUB] + devices = hub.get_device_list() + + _LOGGER.debug("Found a total of %s devices", str(len(devices))) + + entities = (SmartHabCover(cover) + for cover in devices if isinstance(cover, pysmarthab.Shutter)) + + add_entities(entities, True) + + +class SmartHabCover(CoverDevice): + """Representation a cover.""" + + def __init__(self, cover): + """Initialize a SmartHabCover.""" + self._cover = cover + + @property + def unique_id(self) -> str: + """Return a unique ID.""" + return self._cover.device_id + + @property + def name(self) -> str: + """Return the display name of this light.""" + return self._cover.label + + @property + def current_cover_position(self) -> int: + """Return current position of cover. + + None is unknown, 0 is closed, 100 is fully open. + """ + return self._cover.state + + @property + def supported_features(self) -> int: + """Flag supported features.""" + supported_features = SUPPORT_OPEN | SUPPORT_CLOSE + + if self.current_cover_position is not None: + supported_features |= SUPPORT_SET_POSITION + + return supported_features + + @property + def is_closed(self) -> bool: + """Return if the cover is closed or not.""" + return self._cover.state == 0 + + @property + def device_class(self) -> str: + """Return the class of this device, from component DEVICE_CLASSES.""" + return 'window' + + def open_cover(self, **kwargs): + """Open the cover.""" + self._cover.open() + + def close_cover(self, **kwargs): + """Close cover.""" + self._cover.close() + + def set_cover_position(self, **kwargs): + """Move the cover to a specific position.""" + self._cover.state = kwargs[ATTR_POSITION] + + def update(self): + """Fetch new state data for this cover.""" + try: + self._cover.update() + except Timeout: + _LOGGER.error("Reached timeout while updating cover %s from API", + self.entity_id) diff --git a/homeassistant/components/smarthab/light.py b/homeassistant/components/smarthab/light.py new file mode 100644 index 00000000000..9be49912a49 --- /dev/null +++ b/homeassistant/components/smarthab/light.py @@ -0,0 +1,70 @@ +""" +Support for SmartHab device integration. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/smarthab/ +""" +import logging +from datetime import timedelta +from requests.exceptions import Timeout + +from homeassistant.components.light import Light +from . import DOMAIN, DATA_HUB + +_LOGGER = logging.getLogger(__name__) + +SCAN_INTERVAL = timedelta(seconds=60) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the SmartHab lights platform.""" + import pysmarthab + + hub = hass.data[DOMAIN][DATA_HUB] + devices = hub.get_device_list() + + _LOGGER.debug("Found a total of %s devices", str(len(devices))) + + entities = (SmartHabLight(light) + for light in devices if isinstance(light, pysmarthab.Light)) + + add_entities(entities, True) + + +class SmartHabLight(Light): + """Representation of a SmartHab Light.""" + + def __init__(self, light): + """Initialize a SmartHabLight.""" + self._light = light + + @property + def unique_id(self) -> str: + """Return a unique ID.""" + return self._light.device_id + + @property + def name(self) -> str: + """Return the display name of this light.""" + return self._light.label + + @property + def is_on(self) -> bool: + """Return true if light is on.""" + return self._light.state + + def turn_on(self, **kwargs): + """Instruct the light to turn on.""" + self._light.turn_on() + + def turn_off(self, **kwargs): + """Instruct the light to turn off.""" + self._light.turn_off() + + def update(self): + """Fetch new state data for this light.""" + try: + self._light.update() + except Timeout: + _LOGGER.error("Reached timeout while updating light %s from API", + self.entity_id) diff --git a/homeassistant/components/smarthab/manifest.json b/homeassistant/components/smarthab/manifest.json new file mode 100644 index 00000000000..18b587bac92 --- /dev/null +++ b/homeassistant/components/smarthab/manifest.json @@ -0,0 +1,10 @@ +{ + "domain": "smarthab", + "name": "SmartHab", + "documentation": "https://www.home-assistant.io/components/smarthab", + "requirements": [ + "smarthab==0.20" + ], + "dependencies": [], + "codeowners": ["@outadoc"] +} \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index 5a8a72c7cd4..a94dae8ce5a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1617,6 +1617,9 @@ slixmpp==1.4.2 # homeassistant.components.smappee smappy==0.2.16 +# homeassistant.components.smarthab +smarthab==0.20 + # homeassistant.components.bh1750 # homeassistant.components.bme280 # homeassistant.components.bme680