Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions homeassistant/components/dyson.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""Parent component for Dyson Pure Cool Link devices."""

import logging

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT, \
CONF_DEVICES

REQUIREMENTS = ['libpurecoollink==0.1.5']

_LOGGER = logging.getLogger(__name__)

CONF_LANGUAGE = "language"
CONF_RETRY = "retry"

DEFAULT_TIMEOUT = 5
DEFAULT_RETRY = 10

DOMAIN = "dyson"

CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_LANGUAGE): cv.string,
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Optional(CONF_RETRY, default=DEFAULT_RETRY): cv.positive_int,
vol.Optional(CONF_DEVICES, default=[]):
vol.All(cv.ensure_list, [dict]),
})
}, extra=vol.ALLOW_EXTRA)

DYSON_DEVICES = "dyson_devices"


def setup(hass, config):
"""Set up the Dyson parent component."""
_LOGGER.info("Creating new Dyson component")

if DYSON_DEVICES not in hass.data:
hass.data[DYSON_DEVICES] = []

from libpurecoollink.dyson import DysonAccount
dyson_account = DysonAccount(config[DOMAIN].get(CONF_USERNAME),
config[DOMAIN].get(CONF_PASSWORD),
config[DOMAIN].get(CONF_LANGUAGE))

logged = dyson_account.login()

timeout = config[DOMAIN].get(CONF_TIMEOUT)
retry = config[DOMAIN].get(CONF_RETRY)

if not logged:
_LOGGER.error("Not connected to Dyson account. Unable to add devices")
return False

_LOGGER.info("Connected to Dyson account")
dyson_devices = dyson_account.devices()
if CONF_DEVICES in config[DOMAIN] and config[DOMAIN].get(CONF_DEVICES):
configured_devices = config[DOMAIN].get(CONF_DEVICES)
for device in configured_devices:
dyson_device = next((d for d in dyson_devices if
d.serial == device["device_id"]), None)
if dyson_device:
connected = dyson_device.connect(None, device["device_ip"],
timeout, retry)
if connected:
_LOGGER.info("Connected to device %s", dyson_device)
hass.data[DYSON_DEVICES].append(dyson_device)
else:
_LOGGER.warning("Unable to connect to device %s",
dyson_device)
else:
_LOGGER.warning(
"Unable to find device %s in Dyson account",
device["device_id"])
else:
# Not yet reliable
for device in dyson_devices:
_LOGGER.info("Trying to connect to device %s with timeout=%i "
"and retry=%i", device, timeout, retry)
connected = device.connect(None, None, timeout, retry)
if connected:
_LOGGER.info("Connected to device %s", device)
hass.data[DYSON_DEVICES].append(device)
else:
_LOGGER.warning("Unable to connect to device %s", device)

# Start fan/sensors components
if hass.data[DYSON_DEVICES]:
_LOGGER.debug("Starting sensor/fan components")
discovery.load_platform(hass, "sensor", DOMAIN, {}, config)
discovery.load_platform(hass, "fan", DOMAIN, {}, config)

return True
218 changes: 218 additions & 0 deletions homeassistant/components/fan/dyson.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
"""Support for Dyson Pure Cool link fan."""
import logging
import asyncio
from os import path
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.fan import (FanEntity, SUPPORT_OSCILLATE,
SUPPORT_SET_SPEED,
DOMAIN)
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.components.dyson import DYSON_DEVICES
from homeassistant.config import load_yaml_config_file

DEPENDENCIES = ['dyson']

_LOGGER = logging.getLogger(__name__)


DYSON_FAN_DEVICES = "dyson_fan_devices"
SERVICE_SET_NIGHT_MODE = 'dyson_set_night_mode'

DYSON_SET_NIGHT_MODE_SCHEMA = vol.Schema({
vol.Required('entity_id'): cv.entity_id,
vol.Required('night_mode'): cv.boolean
})


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Dyson fan components."""
_LOGGER.info("Creating new Dyson fans")
if DYSON_FAN_DEVICES not in hass.data:
hass.data[DYSON_FAN_DEVICES] = []

# Get Dyson Devices from parent component
for device in hass.data[DYSON_DEVICES]:
dyson_entity = DysonPureCoolLinkDevice(hass, device)
hass.data[DYSON_FAN_DEVICES].append(dyson_entity)

add_devices(hass.data[DYSON_FAN_DEVICES])

descriptions = load_yaml_config_file(
path.join(path.dirname(__file__), 'services.yaml'))

def service_handle(service):
"""Handle dyson services."""
entity_id = service.data.get('entity_id')
night_mode = service.data.get('night_mode')
fan_device = next([fan for fan in hass.data[DYSON_FAN_DEVICES] if
fan.entity_id == entity_id].__iter__(), None)
if fan_device is None:
_LOGGER.warning("Unable to find Dyson fan device %s",
str(entity_id))
return

if service.service == SERVICE_SET_NIGHT_MODE:
fan_device.night_mode(night_mode)

# Register dyson service(s)
hass.services.register(DOMAIN, SERVICE_SET_NIGHT_MODE,
service_handle,
descriptions.get(SERVICE_SET_NIGHT_MODE),
schema=DYSON_SET_NIGHT_MODE_SCHEMA)


class DysonPureCoolLinkDevice(FanEntity):
"""Representation of a Dyson fan."""

def __init__(self, hass, device):
"""Initialize the fan."""
_LOGGER.info("Creating device %s", device.name)
self.hass = hass
self._device = device

@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
self.hass.async_add_job(
self._device.add_message_listener(self.on_message))

def on_message(self, message):
"""Called when new messages received from the fan."""
_LOGGER.debug(
"Message received for fan device %s : %s", self.name, message)
self.schedule_update_ha_state()

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

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

def set_speed(self: ToggleEntity, speed: str) -> None:
"""Set the speed of the fan. Never called ??."""
_LOGGER.debug("Set fan speed to: " + speed)
from libpurecoollink.const import FanSpeed, FanMode
if speed == FanSpeed.FAN_SPEED_AUTO.value:
self._device.set_configuration(fan_mode=FanMode.AUTO)
else:
fan_speed = FanSpeed('{0:04d}'.format(int(speed)))
self._device.set_configuration(fan_mode=FanMode.FAN,
fan_speed=fan_speed)

def turn_on(self: ToggleEntity, speed: str=None, **kwargs) -> None:
"""Turn on the fan."""
_LOGGER.debug("Turn on fan %s with speed %s", self.name, speed)
from libpurecoollink.const import FanSpeed, FanMode
if speed:
if speed == FanSpeed.FAN_SPEED_AUTO.value:
self._device.set_configuration(fan_mode=FanMode.AUTO)
else:
fan_speed = FanSpeed('{0:04d}'.format(int(speed)))
self._device.set_configuration(fan_mode=FanMode.FAN,
fan_speed=fan_speed)
else:
# Speed not set, just turn on
self._device.set_configuration(fan_mode=FanMode.FAN)

def turn_off(self: ToggleEntity, **kwargs) -> None:
"""Turn off the fan."""
_LOGGER.debug("Turn off fan %s", self.name)
from libpurecoollink.const import FanMode
self._device.set_configuration(fan_mode=FanMode.OFF)

def oscillate(self: ToggleEntity, oscillating: bool) -> None:
"""Turn on/off oscillating."""
_LOGGER.debug("Turn oscillation %s for device %s", oscillating,
self.name)
from libpurecoollink.const import Oscillation

if oscillating:
self._device.set_configuration(
oscillation=Oscillation.OSCILLATION_ON)
else:
self._device.set_configuration(
oscillation=Oscillation.OSCILLATION_OFF)

@property
def oscillating(self):
"""Return the oscillation state."""
return self._device.state and self._device.state.oscillation == "ON"

@property
def is_on(self):
"""Return true if the entity is on."""
if self._device.state:
return self._device.state.fan_state == "FAN"
return False

@property
def speed(self) -> str:
"""Return the current speed."""
if self._device.state:
from libpurecoollink.const import FanSpeed
if self._device.state.speed == FanSpeed.FAN_SPEED_AUTO.value:
return self._device.state.speed
else:
return int(self._device.state.speed)
return None

@property
def current_direction(self):
"""Return direction of the fan [forward, reverse]."""
return None

@property
def is_night_mode(self):
"""Return Night mode."""
return self._device.state.night_mode == "ON"

def night_mode(self: ToggleEntity, night_mode: bool) -> None:
"""Turn fan in night mode."""
_LOGGER.debug("Set %s night mode %s", self.name, night_mode)
from libpurecoollink.const import NightMode
if night_mode:
self._device.set_configuration(night_mode=NightMode.NIGHT_MODE_ON)
else:
self._device.set_configuration(night_mode=NightMode.NIGHT_MODE_OFF)

@property
def is_auto_mode(self):
"""Return auto mode."""
return self._device.state.fan_mode == "AUTO"

def auto_mode(self: ToggleEntity, auto_mode: bool) -> None:
"""Turn fan in auto mode."""
_LOGGER.debug("Set %s auto mode %s", self.name, auto_mode)
from libpurecoollink.const import FanMode
if auto_mode:
self._device.set_configuration(fan_mode=FanMode.AUTO)
else:
self._device.set_configuration(fan_mode=FanMode.FAN)

@property
def speed_list(self: ToggleEntity) -> list:
"""Get the list of available speeds."""
from libpurecoollink.const import FanSpeed
supported_speeds = [FanSpeed.FAN_SPEED_AUTO.value,
int(FanSpeed.FAN_SPEED_1.value),
int(FanSpeed.FAN_SPEED_2.value),
int(FanSpeed.FAN_SPEED_3.value),
int(FanSpeed.FAN_SPEED_4.value),
int(FanSpeed.FAN_SPEED_5.value),
int(FanSpeed.FAN_SPEED_6.value),
int(FanSpeed.FAN_SPEED_7.value),
int(FanSpeed.FAN_SPEED_8.value),
int(FanSpeed.FAN_SPEED_9.value),
int(FanSpeed.FAN_SPEED_10.value)]

return supported_speeds

@property
def supported_features(self: ToggleEntity) -> int:
"""Flag supported features."""
return SUPPORT_OSCILLATE | SUPPORT_SET_SPEED
13 changes: 12 additions & 1 deletion homeassistant/components/fan/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,18 @@ set_direction:
fields:
entity_id:
description: Name(s) of the entities to toggle
exampl: 'fan.living_room'
example: 'fan.living_room'
direction:
description: The direction to rotate
example: 'left'

dyson_set_night_mode:
description: Set the fan in night mode

fields:
entity_id:
description: Name(s) of the entities to enable/disable night mode
example: 'fan.living_room'
night_mode:
description: Night mode status
example: true
Loading