Merge pull request #1077 from xrolfex/wink_garage_door_support

Wink Garage Door Support
This commit is contained in:
Paulus Schoutsen 2016-02-11 07:59:57 -08:00
commit 4ce1a67c13
11 changed files with 284 additions and 2 deletions

View File

@ -152,7 +152,7 @@ omit =
homeassistant/components/thermostat/honeywell.py
homeassistant/components/thermostat/proliphix.py
homeassistant/components/thermostat/radiotherm.py
homeassistant/components/garage_door/wink.py
[report]
# Regexes for lines to exclude from consideration

View File

@ -21,6 +21,7 @@ COMPONENTS_WITH_DEMO_PLATFORM = [
'binary_sensor',
'camera',
'device_tracker',
'garage_door',
'light',
'lock',
'media_player',

View File

@ -0,0 +1,108 @@
"""
homeassistant.components.garage_door
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Component to interface with garage doors that can be controlled remotely.
For more details about this component, please refer to the documentation
at https://home-assistant.io/components/garage_door/
"""
import logging
import os
from homeassistant.config import load_yaml_config_file
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN, SERVICE_CLOSE, SERVICE_OPEN,
ATTR_ENTITY_ID)
from homeassistant.components import (group, wink)
DOMAIN = 'garage_door'
SCAN_INTERVAL = 30
GROUP_NAME_ALL_GARAGE_DOORS = 'all garage doors'
ENTITY_ID_ALL_GARAGE_DOORS = group.ENTITY_ID_FORMAT.format('all_garage_doors')
ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
wink.DISCOVER_GARAGE_DOORS: 'wink'
}
_LOGGER = logging.getLogger(__name__)
def is_closed(hass, entity_id=None):
""" Returns if the garage door is closed based on the statemachine. """
entity_id = entity_id or ENTITY_ID_ALL_GARAGE_DOORS
return hass.states.is_state(entity_id, STATE_CLOSED)
def close_door(hass, entity_id=None):
""" Closes all or specified garage door. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.services.call(DOMAIN, SERVICE_CLOSE, data)
def open_door(hass, entity_id=None):
""" Open all or specified garage door. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
hass.services.call(DOMAIN, SERVICE_OPEN, data)
def setup(hass, config):
""" Track states and offer events for garage door. """
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_GARAGE_DOORS)
component.setup(config)
def handle_garage_door_service(service):
""" Handles calls to the garage door services. """
target_locks = component.extract_from_service(service)
for item in target_locks:
if service.service == SERVICE_CLOSE:
item.close_door()
else:
item.open_door()
if item.should_poll:
item.update_ha_state(True)
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_OPEN, handle_garage_door_service,
descriptions.get(SERVICE_OPEN))
hass.services.register(DOMAIN, SERVICE_CLOSE, handle_garage_door_service,
descriptions.get(SERVICE_CLOSE))
return True
class GarageDoorDevice(Entity):
""" Represents a garage door within Home Assistant. """
# pylint: disable=no-self-use
@property
def is_closed(self):
""" Is the garage door closed or opened. """
return None
def close_door(self):
""" Closes the garage door. """
raise NotImplementedError()
def open_door(self):
""" Opens the garage door. """
raise NotImplementedError()
@property
def state(self):
closed = self.is_closed
if closed is None:
return STATE_UNKNOWN
return STATE_CLOSED if closed else STATE_OPEN

View File

@ -0,0 +1,49 @@
"""
homeassistant.components.garage_door.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo platform that has two fake garage doors.
"""
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import STATE_CLOSED, STATE_OPEN
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return demo garage doors. """
add_devices_callback([
DemoGarageDoor('Left Garage Door', STATE_CLOSED),
DemoGarageDoor('Right Garage Door', STATE_OPEN)
])
class DemoGarageDoor(GarageDoorDevice):
""" Provides a demo garage door. """
def __init__(self, name, state):
self._name = name
self._state = state
@property
def should_poll(self):
""" No polling needed for a demo garage door. """
return False
@property
def name(self):
""" Returns the name of the device if any. """
return self._name
@property
def is_closed(self):
""" True if device is closed. """
return self._state == STATE_CLOSED
def close_door(self, **kwargs):
""" Close the device. """
self._state = STATE_CLOSED
self.update_ha_state()
def open_door(self, **kwargs):
""" Open the device. """
self._state = STATE_OPEN
self.update_ha_state()

View File

@ -0,0 +1,67 @@
"""
homeassistant.components.garage_door.wink
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for Wink garage doors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/garage_door.wink/
"""
import logging
from homeassistant.components.garage_door import GarageDoorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
REQUIREMENTS = ['python-wink==0.5.0']
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Wink platform. """
import pywink
if discovery_info is None:
token = config.get(CONF_ACCESS_TOKEN)
if token is None:
logging.getLogger(__name__).error(
"Missing wink access_token. "
"Get one at https://winkbearertoken.appspot.com/")
return
pywink.set_bearer_token(token)
add_devices(WinkGarageDoorDevice(door) for door in
pywink.get_garage_doors())
class WinkGarageDoorDevice(GarageDoorDevice):
""" Represents a Wink garage door. """
def __init__(self, wink):
self.wink = wink
@property
def unique_id(self):
""" Returns the id of this wink garage door """
return "{}.{}".format(self.__class__, self.wink.device_id())
@property
def name(self):
""" Returns the name of the garage door if any. """
return self.wink.name()
def update(self):
""" Update the state of the garage door. """
self.wink.update_state()
@property
def is_closed(self):
""" True if device is closed. """
return self.wink.state() == 0
def close_door(self):
""" Close the device. """
self.wink.set_state(0)
def open_door(self):
""" Open the device. """
self.wink.set_state(1)

View File

@ -22,6 +22,7 @@ DISCOVER_LIGHTS = "wink.lights"
DISCOVER_SWITCHES = "wink.switches"
DISCOVER_SENSORS = "wink.sensors"
DISCOVER_LOCKS = "wink.locks"
DISCOVER_GARAGE_DOORS = "wink.garage_doors"
def setup(hass, config):
@ -42,7 +43,8 @@ def setup(hass, config):
pywink.get_powerstrip_outlets, DISCOVER_SWITCHES),
('sensor', lambda: pywink.get_sensors or
pywink.get_eggtrays, DISCOVER_SENSORS),
('lock', pywink.get_locks, DISCOVER_LOCKS)):
('lock', pywink.get_locks, DISCOVER_LOCKS),
('garage_door', pywink.get_garage_doors, DISCOVER_GARAGE_DOORS)):
if func_exists():
component = get_component(component_name)

View File

@ -151,6 +151,9 @@ SERVICE_ALARM_TRIGGER = "alarm_trigger"
SERVICE_LOCK = "lock"
SERVICE_UNLOCK = "unlock"
SERVICE_OPEN = "open"
SERVICE_CLOSE = "close"
SERVICE_MOVE_UP = 'move_up'
SERVICE_MOVE_DOWN = 'move_down'
SERVICE_STOP = 'stop'

View File

@ -194,6 +194,7 @@ python-telegram-bot==3.2.0
python-twitch==1.2.0
# homeassistant.components.wink
# homeassistant.components.garage_door.wink
# homeassistant.components.light.wink
# homeassistant.components.lock.wink
# homeassistant.components.sensor.wink

View File

View File

@ -0,0 +1,51 @@
"""
tests.components.garage_door.test_demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests demo garage door component.
"""
import unittest
import homeassistant.core as ha
import homeassistant.components.garage_door as gd
LEFT = 'garage_door.left_garage_door'
RIGHT = 'garage_door.right_garage_door'
class TestGarageDoorDemo(unittest.TestCase):
""" Test the demo garage door. """
def setUp(self): # pylint: disable=invalid-name
self.hass = ha.HomeAssistant()
self.assertTrue(gd.setup(self.hass, {
'garage_door': {
'platform': 'demo'
}
}))
def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """
self.hass.stop()
def test_is_closed(self):
self.assertTrue(gd.is_closed(self.hass, LEFT))
self.hass.states.is_state(LEFT, 'close')
self.assertFalse(gd.is_closed(self.hass, RIGHT))
self.hass.states.is_state(RIGHT, 'open')
def test_open_door(self):
gd.open_door(self.hass, LEFT)
self.hass.pool.block_till_done()
self.assertFalse(gd.is_closed(self.hass, LEFT))
def test_close_door(self):
gd.close_door(self.hass, RIGHT)
self.hass.pool.block_till_done()
self.assertTrue(gd.is_closed(self.hass, RIGHT))