Add support Slide cover ()

* Add support GoSlide cover

* Fixed Parameters differ from overridden
Fixed Removed other pylint warnings

* Renamed GoSlide to Slide, because of Innovation in Motion rebranding

* Fixed codeowners file

* Fixed requirements file

* Removed pylint: disable=unused-argument
Removed DOMAIN not exist check
Changed if to min/max
Changed 3rd party import to top of the module
Removed timeout/retry parameters
Removed unused constants
Added check for discovery_info is none
Changed pass slide object instead of full hass object
Changed pass api object instead of full hass object
Added unique_id functionality
Removed entity_id/name properties
Removed supported_features/state functions

* Fixed unused variables

* Changed Slide API uses snake names
Changed Improved exception handling
Changed Updated Slide API to 0.50.0

* Changed moved exceptions into goslide-api
Changed retry setup into coroutine

* Changed str(err) to err
Changed invert if result to if not result
pull/26390/head
Alexander 2019-09-03 19:09:25 +02:00 committed by Paulus Schoutsen
parent 7d1e3af701
commit 330ae0d885
7 changed files with 305 additions and 0 deletions

View File

@ -564,6 +564,7 @@ omit =
homeassistant/components/skybeacon/sensor.py
homeassistant/components/skybell/*
homeassistant/components/slack/notify.py
homeassistant/components/slide/*
homeassistant/components/sma/sensor.py
homeassistant/components/smappee/*
homeassistant/components/smarty/*

View File

@ -236,6 +236,7 @@ homeassistant/components/shell_command/* @home-assistant/core
homeassistant/components/shiftr/* @fabaff
homeassistant/components/shodan/* @fabaff
homeassistant/components/simplisafe/* @bachya
homeassistant/components/slide/* @ualex73
homeassistant/components/sma/* @kellerza
homeassistant/components/smarthab/* @outadoc
homeassistant/components/smartthings/* @andrewsayre

View File

@ -0,0 +1,157 @@
"""Component for the Go Slide API."""
import logging
from datetime import timedelta
import voluptuous as vol
from goslideapi import GoSlideCloud, goslideapi
from homeassistant.const import (
CONF_USERNAME,
CONF_PASSWORD,
CONF_SCAN_INTERVAL,
STATE_OPEN,
STATE_CLOSED,
STATE_OPENING,
STATE_CLOSING,
)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.event import async_track_time_interval, async_call_later
from .const import DOMAIN, SLIDES, API, COMPONENT, DEFAULT_RETRY
_LOGGER = logging.getLogger(__name__)
DEFAULT_SCAN_INTERVAL = timedelta(seconds=30)
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(
CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL
): cv.time_period,
}
)
},
extra=vol.ALLOW_EXTRA,
)
async def async_setup(hass, config):
"""Set up the Slide platform."""
async def update_slides(now=None):
"""Update slide information."""
result = await hass.data[DOMAIN][API].slides_overview()
if result is None:
_LOGGER.error("Slide API does not work or returned an error")
return
if result:
_LOGGER.debug("Slide API returned %d slide(s)", len(result))
else:
_LOGGER.warning("Slide API returned 0 slides")
for slide in result:
if "device_id" not in slide:
_LOGGER.error(
"Found invalid Slide entry, device_id is " "missing. Entry=%s",
slide,
)
continue
uid = slide["device_id"].replace("slide_", "")
slidenew = hass.data[DOMAIN][SLIDES].setdefault(uid, {})
slidenew["mac"] = uid
slidenew["id"] = slide["id"]
slidenew["name"] = slide["device_name"]
slidenew["state"] = None
oldpos = slidenew.get("pos")
slidenew["pos"] = None
slidenew["online"] = False
if "device_info" not in slide:
_LOGGER.error(
"Slide %s (%s) has no device_info Entry=%s",
slide["id"],
slidenew["mac"],
slide,
)
continue
# Check if we have pos (OK) or code (NOK)
if "pos" in slide["device_info"]:
slidenew["online"] = True
slidenew["pos"] = slide["device_info"]["pos"]
slidenew["pos"] = max(0, min(1, slidenew["pos"]))
if oldpos is None or oldpos == slidenew["pos"]:
slidenew["state"] = (
STATE_CLOSED if slidenew["pos"] > 0.95 else STATE_OPEN
)
elif oldpos < slidenew["pos"]:
slidenew["state"] = (
STATE_CLOSED if slidenew["pos"] >= 0.95 else STATE_CLOSING
)
else:
slidenew["state"] = (
STATE_OPEN if slidenew["pos"] <= 0.05 else STATE_OPENING
)
elif "code" in slide["device_info"]:
_LOGGER.warning(
"Slide %s (%s) is offline with " "code=%s",
slide["id"],
slidenew["mac"],
slide["device_info"]["code"],
)
else:
_LOGGER.error(
"Slide %s (%s) has invalid device_info %s",
slide["id"],
slidenew["mac"],
slide["device_info"],
)
_LOGGER.debug("Updated entry=%s", slidenew)
async def retry_setup(now):
"""Retry setup if a connection/timeout happens on Slide API."""
await async_setup(hass, config)
hass.data[DOMAIN] = {}
hass.data[DOMAIN][SLIDES] = {}
username = config[DOMAIN][CONF_USERNAME]
password = config[DOMAIN][CONF_PASSWORD]
scaninterval = config[DOMAIN][CONF_SCAN_INTERVAL]
hass.data[DOMAIN][API] = GoSlideCloud(username, password)
try:
result = await hass.data[DOMAIN][API].login()
except (goslideapi.ClientConnectionError, goslideapi.ClientTimeoutError) as err:
_LOGGER.error(
"Error connecting to Slide Cloud: %s, going to retry in %s seconds",
err,
DEFAULT_RETRY,
)
async_call_later(hass, DEFAULT_RETRY, retry_setup)
return True
if not result:
_LOGGER.error("Slide API returned unknown error during authentication")
return False
_LOGGER.debug("Slide API successfully authenticated")
await update_slides()
hass.async_create_task(async_load_platform(hass, COMPONENT, DOMAIN, {}, config))
async_track_time_interval(hass, update_slides, scaninterval)
return True

View File

@ -0,0 +1,7 @@
"""Define constants for the Go Slide component."""
API = "api"
COMPONENT = "cover"
DOMAIN = "slide"
SLIDES = "slides"
DEFAULT_RETRY = 120

View File

@ -0,0 +1,124 @@
"""Support for Go Slide slides."""
import logging
from homeassistant.const import ATTR_ID
from homeassistant.components.cover import (
ATTR_POSITION,
STATE_CLOSED,
STATE_OPENING,
STATE_CLOSING,
DEVICE_CLASS_CURTAIN,
CoverDevice,
)
from .const import API, DOMAIN, SLIDES
_LOGGER = logging.getLogger(__name__)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up cover(s) for Go Slide platform."""
if discovery_info is None:
return
entities = []
for slide in hass.data[DOMAIN][SLIDES].values():
_LOGGER.debug("Setting up Slide entity: %s", slide)
entities.append(SlideCover(hass.data[DOMAIN][API], slide))
async_add_entities(entities)
class SlideCover(CoverDevice):
"""Representation of a Go Slide cover."""
def __init__(self, api, slide):
"""Initialize the cover."""
self._api = api
self._slide = slide
self._id = slide["id"]
self._unique_id = slide["mac"]
self._name = slide["name"]
@property
def unique_id(self):
"""Return the device unique id."""
return self._unique_id
@property
def name(self):
"""Return the device name."""
return self._name
@property
def device_state_attributes(self):
"""Return device specific state attributes."""
return {ATTR_ID: self._id}
@property
def is_opening(self):
"""Return if the cover is opening or not."""
return self._slide["state"] == STATE_OPENING
@property
def is_closing(self):
"""Return if the cover is closing or not."""
return self._slide["state"] == STATE_CLOSING
@property
def is_closed(self):
"""Return None if status is unknown, True if closed, else False."""
if self._slide["state"] is None:
return None
return self._slide["state"] == STATE_CLOSED
@property
def available(self):
"""Return False if state is not available."""
return self._slide["online"]
@property
def assumed_state(self):
"""Let HA know the integration is assumed state."""
return True
@property
def device_class(self):
"""Return the device class of the cover."""
return DEVICE_CLASS_CURTAIN
@property
def current_cover_position(self):
"""Return the current position of cover shutter."""
pos = self._slide["pos"]
if pos is not None:
pos = int(pos * 100)
return pos
async def async_open_cover(self, **kwargs):
"""Open the cover."""
self._slide["state"] = STATE_OPENING
await self._api.slide_open(self._id)
async def async_close_cover(self, **kwargs):
"""Close the cover."""
self._slide["state"] = STATE_CLOSING
await self._api.slide_close(self._id)
async def async_stop_cover(self, **kwargs):
"""Stop the cover."""
await self._api.slide_stop(self._id)
async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
position = kwargs[ATTR_POSITION] / 100
if self._slide["pos"] is not None:
if position > self._slide["pos"]:
self._slide["state"] = STATE_CLOSING
else:
self._slide["state"] = STATE_OPENING
await self._api.slide_set_position(self._id, position)

View File

@ -0,0 +1,12 @@
{
"domain": "slide",
"name": "Slide",
"documentation": "https://www.home-assistant.io/components/slide",
"requirements": [
"goslide-api==0.5.1"
],
"dependencies": [],
"codeowners": [
"@ualex73"
]
}

View File

@ -567,6 +567,9 @@ google-cloud-texttospeech==0.4.0
# homeassistant.components.google_travel_time
googlemaps==2.5.1
# homeassistant.components.slide
goslide-api==0.5.1
# homeassistant.components.remote_rpi_gpio
gpiozero==1.4.1