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:
parent
9f765836f8
commit
0da8418f3f
7 changed files with 177 additions and 13 deletions
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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:
|
||||
|
|
92
homeassistant/components/fan/wink.py
Normal file
92
homeassistant/components/fan/wink.py
Normal 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
|
|
@ -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'
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue