mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 10:47:10 +00:00
Merge pull request #1077 from xrolfex/wink_garage_door_support
Wink Garage Door Support
This commit is contained in:
commit
4ce1a67c13
@ -152,7 +152,7 @@ omit =
|
|||||||
homeassistant/components/thermostat/honeywell.py
|
homeassistant/components/thermostat/honeywell.py
|
||||||
homeassistant/components/thermostat/proliphix.py
|
homeassistant/components/thermostat/proliphix.py
|
||||||
homeassistant/components/thermostat/radiotherm.py
|
homeassistant/components/thermostat/radiotherm.py
|
||||||
|
homeassistant/components/garage_door/wink.py
|
||||||
|
|
||||||
[report]
|
[report]
|
||||||
# Regexes for lines to exclude from consideration
|
# Regexes for lines to exclude from consideration
|
||||||
|
@ -21,6 +21,7 @@ COMPONENTS_WITH_DEMO_PLATFORM = [
|
|||||||
'binary_sensor',
|
'binary_sensor',
|
||||||
'camera',
|
'camera',
|
||||||
'device_tracker',
|
'device_tracker',
|
||||||
|
'garage_door',
|
||||||
'light',
|
'light',
|
||||||
'lock',
|
'lock',
|
||||||
'media_player',
|
'media_player',
|
||||||
|
108
homeassistant/components/garage_door/__init__.py
Normal file
108
homeassistant/components/garage_door/__init__.py
Normal 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
|
49
homeassistant/components/garage_door/demo.py
Normal file
49
homeassistant/components/garage_door/demo.py
Normal 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()
|
0
homeassistant/components/garage_door/services.yaml
Normal file
0
homeassistant/components/garage_door/services.yaml
Normal file
67
homeassistant/components/garage_door/wink.py
Normal file
67
homeassistant/components/garage_door/wink.py
Normal 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)
|
@ -22,6 +22,7 @@ DISCOVER_LIGHTS = "wink.lights"
|
|||||||
DISCOVER_SWITCHES = "wink.switches"
|
DISCOVER_SWITCHES = "wink.switches"
|
||||||
DISCOVER_SENSORS = "wink.sensors"
|
DISCOVER_SENSORS = "wink.sensors"
|
||||||
DISCOVER_LOCKS = "wink.locks"
|
DISCOVER_LOCKS = "wink.locks"
|
||||||
|
DISCOVER_GARAGE_DOORS = "wink.garage_doors"
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
@ -42,7 +43,8 @@ def setup(hass, config):
|
|||||||
pywink.get_powerstrip_outlets, DISCOVER_SWITCHES),
|
pywink.get_powerstrip_outlets, DISCOVER_SWITCHES),
|
||||||
('sensor', lambda: pywink.get_sensors or
|
('sensor', lambda: pywink.get_sensors or
|
||||||
pywink.get_eggtrays, DISCOVER_SENSORS),
|
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():
|
if func_exists():
|
||||||
component = get_component(component_name)
|
component = get_component(component_name)
|
||||||
|
@ -151,6 +151,9 @@ SERVICE_ALARM_TRIGGER = "alarm_trigger"
|
|||||||
SERVICE_LOCK = "lock"
|
SERVICE_LOCK = "lock"
|
||||||
SERVICE_UNLOCK = "unlock"
|
SERVICE_UNLOCK = "unlock"
|
||||||
|
|
||||||
|
SERVICE_OPEN = "open"
|
||||||
|
SERVICE_CLOSE = "close"
|
||||||
|
|
||||||
SERVICE_MOVE_UP = 'move_up'
|
SERVICE_MOVE_UP = 'move_up'
|
||||||
SERVICE_MOVE_DOWN = 'move_down'
|
SERVICE_MOVE_DOWN = 'move_down'
|
||||||
SERVICE_STOP = 'stop'
|
SERVICE_STOP = 'stop'
|
||||||
|
@ -194,6 +194,7 @@ python-telegram-bot==3.2.0
|
|||||||
python-twitch==1.2.0
|
python-twitch==1.2.0
|
||||||
|
|
||||||
# homeassistant.components.wink
|
# homeassistant.components.wink
|
||||||
|
# homeassistant.components.garage_door.wink
|
||||||
# homeassistant.components.light.wink
|
# homeassistant.components.light.wink
|
||||||
# homeassistant.components.lock.wink
|
# homeassistant.components.lock.wink
|
||||||
# homeassistant.components.sensor.wink
|
# homeassistant.components.sensor.wink
|
||||||
|
0
tests/components/garage_door/__init__.py
Normal file
0
tests/components/garage_door/__init__.py
Normal file
51
tests/components/garage_door/test_demo.py
Normal file
51
tests/components/garage_door/test_demo.py
Normal 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))
|
Loading…
x
Reference in New Issue
Block a user