Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic support for IKEA Fyrtur blinds #26659

Merged
merged 10 commits into from Sep 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Expand Up @@ -669,6 +669,7 @@ omit =
homeassistant/components/trackr/device_tracker.py
homeassistant/components/tradfri/*
homeassistant/components/tradfri/light.py
homeassistant/components/tradfri/cover.py
homeassistant/components/trafikverket_train/sensor.py
homeassistant/components/trafikverket_weatherstation/sensor.py
homeassistant/components/transmission/*
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/tradfri/__init__.py
Expand Up @@ -131,6 +131,9 @@ async def on_hass_stop(event):
sw_version=gateway_info.firmware_version,
)

hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "cover")
)
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "light")
)
Expand Down
149 changes: 149 additions & 0 deletions homeassistant/components/tradfri/cover.py
@@ -0,0 +1,149 @@
"""Support for IKEA Tradfri covers."""
import logging

from pytradfri.error import PytradfriError

from homeassistant.components.cover import (
CoverDevice,
ATTR_POSITION,
SUPPORT_OPEN,
SUPPORT_CLOSE,
SUPPORT_SET_POSITION,
)
from homeassistant.core import callback
from . import DOMAIN as TRADFRI_DOMAIN, KEY_API, KEY_GATEWAY
from .const import CONF_GATEWAY_ID

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass, config_entry, async_add_entities):
"""Load Tradfri covers based on a config entry."""
gateway_id = config_entry.data[CONF_GATEWAY_ID]
api = hass.data[KEY_API][config_entry.entry_id]
gateway = hass.data[KEY_GATEWAY][config_entry.entry_id]

devices_commands = await api(gateway.get_devices())
devices = await api(devices_commands)
covers = [dev for dev in devices if dev.has_blind_control]
if covers:
async_add_entities(TradfriCover(cover, api, gateway_id) for cover in covers)


class TradfriCover(CoverDevice):
"""The platform class required by Home Assistant."""

def __init__(self, cover, api, gateway_id):
"""Initialize a cover."""
self._api = api
self._unique_id = f"{gateway_id}-{cover.id}"
self._cover = None
self._cover_control = None
self._cover_data = None
self._name = None
self._available = True
self._gateway_id = gateway_id

self._refresh(cover)

@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION

@property
def unique_id(self):
"""Return unique ID for cover."""
return self._unique_id

@property
def device_info(self):
"""Return the device info."""
info = self._cover.device_info

return {
"identifiers": {(TRADFRI_DOMAIN, self._cover.id)},
"name": self._name,
"manufacturer": info.manufacturer,
"model": info.model_number,
"sw_version": info.firmware_version,
"via_device": (TRADFRI_DOMAIN, self._gateway_id),
}

async def async_added_to_hass(self):
"""Start thread when added to hass."""
self._async_start_observe()

@property
def available(self):
"""Return True if entity is available."""
return self._available

@property
def should_poll(self):
"""No polling needed for tradfri cover."""
return False

@property
def name(self):
"""Return the display name of this cover."""
return self._name

@property
def current_cover_position(self):
"""Return current position of cover.

None is unknown, 0 is closed, 100 is fully open.
ggravlingen marked this conversation as resolved.
Show resolved Hide resolved
"""
return 100 - self._cover_data.current_cover_position

async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
await self._api(self._cover_control.set_state(100 - kwargs[ATTR_POSITION]))

async def async_open_cover(self, **kwargs):
"""Open the cover."""
await self._api(self._cover_control.set_state(0))

async def async_close_cover(self, **kwargs):
"""Close cover."""
await self._api(self._cover_control.set_state(100))

@property
def is_closed(self):
"""Return if the cover is closed or not."""
return self.current_cover_position == 0

@callback
def _async_start_observe(self, exc=None):
"""Start observation of cover."""
if exc:
self._available = False
self.async_schedule_update_ha_state()
_LOGGER.warning("Observation failed for %s", self._name, exc_info=exc)
try:
cmd = self._cover.observe(
callback=self._observe_update,
err_callback=self._async_start_observe,
duration=0,
)
self.hass.async_create_task(self._api(cmd))
except PytradfriError as err:
_LOGGER.warning("Observation failed, trying again", exc_info=err)
self._async_start_observe()

def _refresh(self, cover):
"""Refresh the cover data."""
self._cover = cover

# Caching of BlindControl and cover object
self._available = cover.reachable
self._cover_control = cover.blind_control
self._cover_data = cover.blind_control.blinds[0]
self._name = cover.name

@callback
def _observe_update(self, tradfri_device):
"""Receive new state data for this cover."""
self._refresh(tradfri_device)
self.async_schedule_update_ha_state()
11 changes: 3 additions & 8 deletions homeassistant/components/tradfri/light.py
@@ -1,6 +1,9 @@
"""Support for IKEA Tradfri lights."""
import logging

from pytradfri.error import PytradfriError

import homeassistant.util.color as color_util
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP,
Expand All @@ -14,8 +17,6 @@
Light,
)
from homeassistant.core import callback
import homeassistant.util.color as color_util

from . import DOMAIN as TRADFRI_DOMAIN, KEY_API, KEY_GATEWAY
from .const import CONF_GATEWAY_ID, CONF_IMPORT_GROUPS

Expand All @@ -26,7 +27,6 @@
ATTR_SAT = "saturation"
ATTR_TRANSITION_TIME = "transition_time"
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA
IKEA = "IKEA of Sweden"
TRADFRI_LIGHT_MANAGER = "Tradfri Light Manager"
SUPPORTED_FEATURES = SUPPORT_TRANSITION
SUPPORTED_GROUP_FEATURES = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION
Expand Down Expand Up @@ -113,9 +113,6 @@ async def async_turn_on(self, **kwargs):
@callback
def _async_start_observe(self, exc=None):
"""Start observation of light."""
# pylint: disable=import-error
from pytradfri.error import PytradfriError

if exc:
_LOGGER.warning("Observation failed for %s", self._name, exc_info=exc)

Expand Down Expand Up @@ -339,8 +336,6 @@ async def async_turn_on(self, **kwargs):
@callback
def _async_start_observe(self, exc=None):
"""Start observation of light."""
# pylint: disable=import-error
from pytradfri.error import PytradfriError

if exc:
self._available = False
Expand Down
8 changes: 3 additions & 5 deletions homeassistant/components/tradfri/sensor.py
@@ -1,10 +1,11 @@
"""Support for IKEA Tradfri sensors."""
from datetime import timedelta
import logging
from datetime import timedelta

from pytradfri.error import PytradfriError

from homeassistant.core import callback
from homeassistant.helpers.entity import Entity

from . import KEY_API, KEY_GATEWAY

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -79,9 +80,6 @@ def state(self):
@callback
def _async_start_observe(self, exc=None):
"""Start observation of light."""
# pylint: disable=import-error
from pytradfri.error import PytradfriError

if exc:
_LOGGER.warning("Observation failed for %s", self._name, exc_info=exc)

Expand Down
6 changes: 2 additions & 4 deletions homeassistant/components/tradfri/switch.py
@@ -1,15 +1,15 @@
"""Support for IKEA Tradfri switches."""
import logging

from pytradfri.error import PytradfriError

from homeassistant.components.switch import SwitchDevice
from homeassistant.core import callback

from . import DOMAIN as TRADFRI_DOMAIN, KEY_API, KEY_GATEWAY
from .const import CONF_GATEWAY_ID

_LOGGER = logging.getLogger(__name__)

IKEA = "IKEA of Sweden"
TRADFRI_SWITCH_MANAGER = "Tradfri Switch Manager"


Expand Down Expand Up @@ -98,8 +98,6 @@ async def async_turn_on(self, **kwargs):
@callback
def _async_start_observe(self, exc=None):
"""Start observation of switch."""
from pytradfri.error import PytradfriError

if exc:
self._available = False
self.async_schedule_update_ha_state()
Expand Down