Wink fan support (#5174)

* Initial commit for Wink fan support

* Added fan to discovery list

* Raise NotImplementedError and fixed is_on

* Added speed property

* Update __init__.py
This commit is contained in:
William Scanlon 2017-01-14 01:08:13 -05:00 committed by Paulus Schoutsen
parent 9f765836f8
commit 0da8418f3f
7 changed files with 177 additions and 13 deletions

View file

@ -33,9 +33,11 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
ATTR_SUPPORTED_FEATURES = 'supported_features'
SUPPORT_SET_SPEED = 1
SUPPORT_OSCILLATE = 2
SUPPORT_DIRECTION = 4
SERVICE_SET_SPEED = 'set_speed'
SERVICE_OSCILLATE = 'oscillate'
SERVICE_SET_DIRECTION = 'set_direction'
SPEED_OFF = 'off'
SPEED_LOW = 'low'
@ -43,15 +45,20 @@ SPEED_MED = 'med'
SPEED_MEDIUM = 'medium'
SPEED_HIGH = 'high'
DIRECTION_FORWARD = 'forward'
DIRECTION_REVERSE = 'reverse'
ATTR_SPEED = 'speed'
ATTR_SPEED_LIST = 'speed_list'
ATTR_OSCILLATING = 'oscillating'
ATTR_DIRECTION = 'direction'
PROP_TO_ATTR = {
'speed': ATTR_SPEED,
'speed_list': ATTR_SPEED_LIST,
'oscillating': ATTR_OSCILLATING,
'supported_features': ATTR_SUPPORTED_FEATURES,
'direction': ATTR_DIRECTION,
} # type: dict
FAN_SET_SPEED_SCHEMA = vol.Schema({
@ -77,6 +84,11 @@ FAN_TOGGLE_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID): cv.entity_ids
})
FAN_SET_DIRECTION_SCHEMA = vol.Schema({
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_DIRECTION): cv.string
}) # type: dict
_LOGGER = logging.getLogger(__name__)
@ -141,6 +153,18 @@ def set_speed(hass, entity_id: str=None, speed: str=None) -> None:
hass.services.call(DOMAIN, SERVICE_SET_SPEED, data)
def set_direction(hass, entity_id: str=None, direction: str=None) -> None:
"""Set direction for all or specified fan."""
data = {
key: value for key, value in [
(ATTR_ENTITY_ID, entity_id),
(ATTR_DIRECTION, direction),
] if value is not None
}
hass.services.call(DOMAIN, SERVICE_SET_DIRECTION, data)
def setup(hass, config: dict) -> None:
"""Expose fan control via statemachine and services."""
component = EntityComponent(
@ -158,7 +182,8 @@ def setup(hass, config: dict) -> None:
service_fun = None
for service_def in [SERVICE_TURN_ON, SERVICE_TURN_OFF,
SERVICE_SET_SPEED, SERVICE_OSCILLATE]:
SERVICE_SET_SPEED, SERVICE_OSCILLATE,
SERVICE_SET_DIRECTION]:
if service_def == service.service:
service_fun = service_def
break
@ -191,6 +216,10 @@ def setup(hass, config: dict) -> None:
descriptions.get(SERVICE_OSCILLATE),
schema=FAN_OSCILLATE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_SET_DIRECTION, handle_fan_service,
descriptions.get(SERVICE_SET_DIRECTION),
schema=FAN_SET_DIRECTION_SCHEMA)
return True
@ -201,7 +230,11 @@ class FanEntity(ToggleEntity):
def set_speed(self: ToggleEntity, speed: str) -> None:
"""Set the speed of the fan."""
pass
raise NotImplementedError()
def set_direction(self: ToggleEntity, direction: str) -> None:
"""Set the direction of the fan."""
raise NotImplementedError()
def turn_on(self: ToggleEntity, speed: str=None, **kwargs) -> None:
"""Turn on the fan."""
@ -218,14 +251,23 @@ class FanEntity(ToggleEntity):
@property
def is_on(self):
"""Return true if the entity is on."""
return self.state_attributes.get(ATTR_SPEED, STATE_UNKNOWN) \
not in [SPEED_OFF, STATE_UNKNOWN]
return self.speed not in [SPEED_OFF, STATE_UNKNOWN]
@property
def speed(self) -> str:
"""Return the current speed."""
return None
@property
def speed_list(self: ToggleEntity) -> list:
"""Get the list of available speeds."""
return []
@property
def current_direction(self) -> str:
"""Return the current direction of the fan."""
return None
@property
def state_attributes(self: ToggleEntity) -> dict:
"""Return optional state attributes."""

View file

@ -7,14 +7,14 @@ https://home-assistant.io/components/demo/
from homeassistant.components.fan import (SPEED_LOW, SPEED_MED, SPEED_HIGH,
FanEntity, SUPPORT_SET_SPEED,
SUPPORT_OSCILLATE)
SUPPORT_OSCILLATE, SUPPORT_DIRECTION)
from homeassistant.const import STATE_OFF
FAN_NAME = 'Living Room Fan'
FAN_ENTITY_ID = 'fan.living_room_fan'
DEMO_SUPPORT = SUPPORT_SET_SPEED | SUPPORT_OSCILLATE
DEMO_SUPPORT = SUPPORT_SET_SPEED | SUPPORT_OSCILLATE | SUPPORT_DIRECTION
# pylint: disable=unused-argument
@ -31,8 +31,9 @@ class DemoFan(FanEntity):
def __init__(self, hass, name: str, initial_state: str) -> None:
"""Initialize the entity."""
self.hass = hass
self.speed = initial_state
self._speed = initial_state
self.oscillating = False
self.direction = "forward"
self._name = name
@property
@ -45,6 +46,11 @@ class DemoFan(FanEntity):
"""No polling needed for a demo fan."""
return False
@property
def speed(self) -> str:
"""Return the current speed."""
return self._speed
@property
def speed_list(self) -> list:
"""Get the list of available speeds."""
@ -61,7 +67,12 @@ class DemoFan(FanEntity):
def set_speed(self, speed: str) -> None:
"""Set the speed of the fan."""
self.speed = speed
self._speed = speed
self.update_ha_state()
def set_direction(self, direction: str) -> None:
"""Set the direction of the fan."""
self.direction = direction
self.update_ha_state()
def oscillate(self, oscillating: bool) -> None:
@ -69,6 +80,11 @@ class DemoFan(FanEntity):
self.oscillating = oscillating
self.update_ha_state()
@property
def current_direction(self) -> str:
"""Fan direction."""
return self.direction
@property
def supported_features(self) -> int:
"""Flag supported features."""

View file

@ -64,7 +64,11 @@ class ISYFanDevice(isy.ISYDevice, FanEntity):
def __init__(self, node) -> None:
"""Initialize the ISY994 fan device."""
isy.ISYDevice.__init__(self, node)
self.speed = self.state
@property
def speed(self) -> str:
"""Return the current speed."""
return self.state
@property
def state(self) -> str:

View file

@ -0,0 +1,92 @@
"""
Support for Wink fans.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/fan.wink/
"""
import logging
from homeassistant.components.fan import (FanEntity, SPEED_HIGH,
SPEED_LOW, SPEED_MEDIUM,
STATE_UNKNOWN)
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.components.wink import WinkDevice
_LOGGER = logging.getLogger(__name__)
SPEED_LOWEST = "lowest"
SPEED_AUTO = "auto"
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Wink platform."""
import pywink
add_devices(WinkFanDevice(fan, hass) for fan in pywink.get_fans())
class WinkFanDevice(WinkDevice, FanEntity):
"""Representation of a Wink fan."""
def __init__(self, wink, hass):
"""Initialize the fan."""
WinkDevice.__init__(self, wink, hass)
def set_drection(self: ToggleEntity, direction: str) -> None:
"""Set the direction of the fan."""
self.wink.set_fan_direction(direction)
def set_speed(self: ToggleEntity, speed: str) -> None:
"""Set the speed of the fan."""
self.wink.set_fan_speed(speed)
def turn_on(self: ToggleEntity, speed: str=None, **kwargs) -> None:
"""Turn on the fan."""
self.wink.set_state(True)
def turn_off(self: ToggleEntity, **kwargs) -> None:
"""Turn off the fan."""
self.wink.set_state(False)
@property
def is_on(self):
"""Return true if the entity is on."""
return self.wink.state()
@property
def speed(self) -> str:
"""Return the current speed."""
current_wink_speed = self.wink.current_fan_speed()
if SPEED_AUTO == current_wink_speed:
return SPEED_AUTO
if SPEED_LOWEST == current_wink_speed:
return SPEED_LOWEST
if SPEED_LOW == current_wink_speed:
return SPEED_LOW
if SPEED_MEDIUM == current_wink_speed:
return SPEED_MEDIUM
if SPEED_HIGH == current_wink_speed:
return SPEED_HIGH
return STATE_UNKNOWN
@property
def current_direction(self):
"""Return direction of the fan [forward, reverse]."""
return self.wink.current_fan_direction()
@property
def speed_list(self: ToggleEntity) -> list:
"""Get the list of available speeds."""
wink_supported_speeds = self.wink.fan_speeds()
supported_speeds = []
if SPEED_AUTO in wink_supported_speeds:
supported_speeds.append(SPEED_AUTO)
if SPEED_LOWEST in wink_supported_speeds:
supported_speeds.append(SPEED_LOWEST)
if SPEED_LOW in wink_supported_speeds:
supported_speeds.append(SPEED_LOW)
if SPEED_MEDIUM in wink_supported_speeds:
supported_speeds.append(SPEED_MEDIUM)
if SPEED_HIGH in wink_supported_speeds:
supported_speeds.append(SPEED_HIGH)
return supported_speeds

View file

@ -15,7 +15,7 @@ from homeassistant.const import (
from homeassistant.helpers.entity import Entity
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['python-wink==0.11.0', 'pubnubsub-handler==0.0.5']
REQUIREMENTS = ['python-wink==0.12.0', 'pubnubsub-handler==0.0.7']
_LOGGER = logging.getLogger(__name__)
@ -50,7 +50,8 @@ CONFIG_SCHEMA = vol.Schema({
}, extra=vol.ALLOW_EXTRA)
WINK_COMPONENTS = [
'binary_sensor', 'sensor', 'light', 'switch', 'lock', 'cover', 'climate'
'binary_sensor', 'sensor', 'light', 'switch', 'lock', 'cover', 'climate',
'fan'
]

View file

@ -362,7 +362,7 @@ proliphix==0.4.1
psutil==5.0.1
# homeassistant.components.wink
pubnubsub-handler==0.0.5
pubnubsub-handler==0.0.7
# homeassistant.components.notify.pushbullet
pushbullet.py==0.10.0
@ -512,7 +512,7 @@ python-twitch==1.3.0
python-vlc==1.1.2
# homeassistant.components.wink
python-wink==0.11.0
python-wink==0.12.0
# homeassistant.components.device_tracker.trackr
pytrackr==0.0.5

View file

@ -55,6 +55,15 @@ class TestDemoFan(unittest.TestCase):
self.hass.block_till_done()
self.assertEqual(STATE_OFF, self.get_entity().state)
def test_set_direction(self):
"""Test setting the direction of the device."""
self.assertEqual(STATE_OFF, self.get_entity().state)
fan.set_direction(self.hass, FAN_ENTITY_ID, fan.DIRECTION_REVERSE)
self.hass.block_till_done()
self.assertEqual(fan.DIRECTION_REVERSE,
self.get_entity().attributes.get('direction'))
def test_set_speed(self):
"""Test setting the speed of the device."""
self.assertEqual(STATE_OFF, self.get_entity().state)