Refactor: code moved to new helper and constants file. Also adds support for multiple types for switch/light components.
This commit is contained in:
parent
513a03fb46
commit
0527760e9b
41 changed files with 603 additions and 442 deletions
|
@ -55,6 +55,8 @@ After you got the demo mode running it is time to enable some real components an
|
||||||
|
|
||||||
*Note:* you can append `?api_password=YOUR_PASSWORD` to the url of the web interface to log in automatically.
|
*Note:* you can append `?api_password=YOUR_PASSWORD` to the url of the web interface to log in automatically.
|
||||||
|
|
||||||
|
*Note:* for the light and switch component, you can specify multiple types by using sequential sections: [switch], [switch 2], [switch 3] etc
|
||||||
|
|
||||||
### Philips Hue
|
### Philips Hue
|
||||||
To get Philips Hue working you will have to connect Home Assistant to the Hue bridge.
|
To get Philips Hue working you will have to connect Home Assistant to the Hue bridge.
|
||||||
|
|
||||||
|
@ -68,7 +70,7 @@ After that add the following lines to your `home-assistant.conf`:
|
||||||
|
|
||||||
```
|
```
|
||||||
[light]
|
[light]
|
||||||
type=hue
|
platform=hue
|
||||||
```
|
```
|
||||||
|
|
||||||
### Wireless router
|
### Wireless router
|
||||||
|
@ -77,7 +79,7 @@ Your wireless router is used to track which devices are connected. Three differe
|
||||||
|
|
||||||
```
|
```
|
||||||
[device_tracker]
|
[device_tracker]
|
||||||
type=netgear
|
platform=netgear
|
||||||
host=192.168.1.1
|
host=192.168.1.1
|
||||||
username=admin
|
username=admin
|
||||||
password=MY_PASSWORD
|
password=MY_PASSWORD
|
||||||
|
|
|
@ -9,11 +9,11 @@ api_password=mypass
|
||||||
# development=1
|
# development=1
|
||||||
|
|
||||||
[light]
|
[light]
|
||||||
type=hue
|
platform=hue
|
||||||
|
|
||||||
[device_tracker]
|
[device_tracker]
|
||||||
# The following types are available: netgear, tomato, luci
|
# The following types are available: netgear, tomato, luci
|
||||||
type=netgear
|
platform=netgear
|
||||||
host=192.168.1.1
|
host=192.168.1.1
|
||||||
username=admin
|
username=admin
|
||||||
password=PASSWORD
|
password=PASSWORD
|
||||||
|
@ -26,7 +26,7 @@ password=PASSWORD
|
||||||
# hosts=192.168.1.9,192.168.1.12
|
# hosts=192.168.1.9,192.168.1.12
|
||||||
|
|
||||||
[switch]
|
[switch]
|
||||||
type=wemo
|
platform=wemo
|
||||||
# Optional: hard code the hosts (comma seperated) to avoid scanning the network
|
# Optional: hard code the hosts (comma seperated) to avoid scanning the network
|
||||||
# hosts=192.168.1.9,192.168.1.12
|
# hosts=192.168.1.9,192.168.1.12
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ Provides a mock switch platform.
|
||||||
|
|
||||||
Call init before using it in your tests to ensure clean test data.
|
Call init before using it in your tests to ensure clean test data.
|
||||||
"""
|
"""
|
||||||
import homeassistant.components as components
|
from homeassistant.const import STATE_ON, STATE_OFF
|
||||||
from ha_test.helper import MockToggleDevice
|
from ha_test.helpers import MockToggleDevice
|
||||||
|
|
||||||
|
|
||||||
DEVICES = []
|
DEVICES = []
|
||||||
|
@ -18,9 +18,9 @@ def init(empty=False):
|
||||||
global DEVICES
|
global DEVICES
|
||||||
|
|
||||||
DEVICES = [] if empty else [
|
DEVICES = [] if empty else [
|
||||||
MockToggleDevice('Ceiling', components.STATE_ON),
|
MockToggleDevice('Ceiling', STATE_ON),
|
||||||
MockToggleDevice('Ceiling', components.STATE_OFF),
|
MockToggleDevice('Ceiling', STATE_OFF),
|
||||||
MockToggleDevice(None, components.STATE_OFF)
|
MockToggleDevice(None, STATE_OFF)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ Provides a mock switch platform.
|
||||||
|
|
||||||
Call init before using it in your tests to ensure clean test data.
|
Call init before using it in your tests to ensure clean test data.
|
||||||
"""
|
"""
|
||||||
import homeassistant.components as components
|
from homeassistant.const import STATE_ON, STATE_OFF
|
||||||
from ha_test.helper import MockToggleDevice
|
from ha_test.helpers import MockToggleDevice
|
||||||
|
|
||||||
|
|
||||||
DEVICES = []
|
DEVICES = []
|
||||||
|
@ -18,9 +18,9 @@ def init(empty=False):
|
||||||
global DEVICES
|
global DEVICES
|
||||||
|
|
||||||
DEVICES = [] if empty else [
|
DEVICES = [] if empty else [
|
||||||
MockToggleDevice('AC', components.STATE_ON),
|
MockToggleDevice('AC', STATE_ON),
|
||||||
MockToggleDevice('AC', components.STATE_OFF),
|
MockToggleDevice('AC', STATE_OFF),
|
||||||
MockToggleDevice(None, components.STATE_OFF)
|
MockToggleDevice(None, STATE_OFF)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ Helper method for writing tests.
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.components as components
|
from homeassistant.helpers import ToggleDevice
|
||||||
|
from homeassistant.const import STATE_ON, STATE_OFF
|
||||||
|
|
||||||
|
|
||||||
def get_test_home_assistant():
|
def get_test_home_assistant():
|
||||||
|
@ -41,7 +42,7 @@ class MockModule(object):
|
||||||
self.setup = lambda hass, config: False if setup is None else setup
|
self.setup = lambda hass, config: False if setup is None else setup
|
||||||
|
|
||||||
|
|
||||||
class MockToggleDevice(components.ToggleDevice):
|
class MockToggleDevice(ToggleDevice):
|
||||||
""" Provides a mock toggle device. """
|
""" Provides a mock toggle device. """
|
||||||
def __init__(self, name, state):
|
def __init__(self, name, state):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -56,17 +57,17 @@ class MockToggleDevice(components.ToggleDevice):
|
||||||
def turn_on(self, **kwargs):
|
def turn_on(self, **kwargs):
|
||||||
""" Turn the device on. """
|
""" Turn the device on. """
|
||||||
self.calls.append(('turn_on', kwargs))
|
self.calls.append(('turn_on', kwargs))
|
||||||
self.state = components.STATE_ON
|
self.state = STATE_ON
|
||||||
|
|
||||||
def turn_off(self, **kwargs):
|
def turn_off(self, **kwargs):
|
||||||
""" Turn the device off. """
|
""" Turn the device off. """
|
||||||
self.calls.append(('turn_off', kwargs))
|
self.calls.append(('turn_off', kwargs))
|
||||||
self.state = components.STATE_OFF
|
self.state = STATE_OFF
|
||||||
|
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
""" True if device is on. """
|
""" True if device is on. """
|
||||||
self.calls.append(('is_on', {}))
|
self.calls.append(('is_on', {}))
|
||||||
return self.state == components.STATE_ON
|
return self.state == STATE_ON
|
||||||
|
|
||||||
def last_call(self, method=None):
|
def last_call(self, method=None):
|
||||||
if method is None:
|
if method is None:
|
|
@ -9,9 +9,13 @@ import logging
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.components as components
|
from homeassistant.const import (
|
||||||
|
SERVICE_TURN_OFF, SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN,
|
||||||
|
SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE,
|
||||||
|
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREV_TRACK, ATTR_ENTITY_ID,
|
||||||
|
CONF_HOSTS)
|
||||||
import homeassistant.components.chromecast as chromecast
|
import homeassistant.components.chromecast as chromecast
|
||||||
from helper import mock_service
|
from helpers import mock_service
|
||||||
|
|
||||||
|
|
||||||
def setUpModule(): # pylint: disable=invalid-name
|
def setUpModule(): # pylint: disable=invalid-name
|
||||||
|
@ -45,14 +49,14 @@ class TestChromecast(unittest.TestCase):
|
||||||
Test if the call service methods conver to correct service calls.
|
Test if the call service methods conver to correct service calls.
|
||||||
"""
|
"""
|
||||||
services = {
|
services = {
|
||||||
components.SERVICE_TURN_OFF: chromecast.turn_off,
|
SERVICE_TURN_OFF: chromecast.turn_off,
|
||||||
components.SERVICE_VOLUME_UP: chromecast.volume_up,
|
SERVICE_VOLUME_UP: chromecast.volume_up,
|
||||||
components.SERVICE_VOLUME_DOWN: chromecast.volume_down,
|
SERVICE_VOLUME_DOWN: chromecast.volume_down,
|
||||||
components.SERVICE_MEDIA_PLAY_PAUSE: chromecast.media_play_pause,
|
SERVICE_MEDIA_PLAY_PAUSE: chromecast.media_play_pause,
|
||||||
components.SERVICE_MEDIA_PLAY: chromecast.media_play,
|
SERVICE_MEDIA_PLAY: chromecast.media_play,
|
||||||
components.SERVICE_MEDIA_PAUSE: chromecast.media_pause,
|
SERVICE_MEDIA_PAUSE: chromecast.media_pause,
|
||||||
components.SERVICE_MEDIA_NEXT_TRACK: chromecast.media_next_track,
|
SERVICE_MEDIA_NEXT_TRACK: chromecast.media_next_track,
|
||||||
components.SERVICE_MEDIA_PREV_TRACK: chromecast.media_prev_track
|
SERVICE_MEDIA_PREV_TRACK: chromecast.media_prev_track
|
||||||
}
|
}
|
||||||
|
|
||||||
for service_name, service_method in services.items():
|
for service_name, service_method in services.items():
|
||||||
|
@ -75,7 +79,7 @@ class TestChromecast(unittest.TestCase):
|
||||||
self.assertEqual(call.domain, chromecast.DOMAIN)
|
self.assertEqual(call.domain, chromecast.DOMAIN)
|
||||||
self.assertEqual(call.service, service_name)
|
self.assertEqual(call.service, service_name)
|
||||||
self.assertEqual(call.data,
|
self.assertEqual(call.data,
|
||||||
{components.ATTR_ENTITY_ID: self.test_entity})
|
{ATTR_ENTITY_ID: self.test_entity})
|
||||||
|
|
||||||
def test_setup(self):
|
def test_setup(self):
|
||||||
"""
|
"""
|
||||||
|
@ -84,4 +88,4 @@ class TestChromecast(unittest.TestCase):
|
||||||
In an ideal world we would create a mock pychromecast API..
|
In an ideal world we would create a mock pychromecast API..
|
||||||
"""
|
"""
|
||||||
self.assertFalse(chromecast.setup(
|
self.assertFalse(chromecast.setup(
|
||||||
self.hass, {chromecast.DOMAIN: {ha.CONF_HOSTS: '127.0.0.1'}}))
|
self.hass, {chromecast.DOMAIN: {CONF_HOSTS: '127.0.0.1'}}))
|
||||||
|
|
|
@ -9,6 +9,8 @@ import unittest
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
|
from homeassistant.const import (
|
||||||
|
STATE_ON, STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF)
|
||||||
import homeassistant.components as comps
|
import homeassistant.components as comps
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,8 +23,8 @@ class TestComponentsCore(unittest.TestCase):
|
||||||
loader.prepare(self.hass)
|
loader.prepare(self.hass)
|
||||||
self.assertTrue(comps.setup(self.hass, {}))
|
self.assertTrue(comps.setup(self.hass, {}))
|
||||||
|
|
||||||
self.hass.states.set('light.Bowl', comps.STATE_ON)
|
self.hass.states.set('light.Bowl', STATE_ON)
|
||||||
self.hass.states.set('light.Ceiling', comps.STATE_OFF)
|
self.hass.states.set('light.Ceiling', STATE_OFF)
|
||||||
|
|
||||||
def tearDown(self): # pylint: disable=invalid-name
|
def tearDown(self): # pylint: disable=invalid-name
|
||||||
""" Stop down stuff we started. """
|
""" Stop down stuff we started. """
|
||||||
|
@ -38,7 +40,7 @@ class TestComponentsCore(unittest.TestCase):
|
||||||
""" Test turn_on method. """
|
""" Test turn_on method. """
|
||||||
runs = []
|
runs = []
|
||||||
self.hass.services.register(
|
self.hass.services.register(
|
||||||
'light', comps.SERVICE_TURN_ON, lambda x: runs.append(1))
|
'light', SERVICE_TURN_ON, lambda x: runs.append(1))
|
||||||
|
|
||||||
comps.turn_on(self.hass, 'light.Ceiling')
|
comps.turn_on(self.hass, 'light.Ceiling')
|
||||||
|
|
||||||
|
@ -50,24 +52,10 @@ class TestComponentsCore(unittest.TestCase):
|
||||||
""" Test turn_off method. """
|
""" Test turn_off method. """
|
||||||
runs = []
|
runs = []
|
||||||
self.hass.services.register(
|
self.hass.services.register(
|
||||||
'light', comps.SERVICE_TURN_OFF, lambda x: runs.append(1))
|
'light', SERVICE_TURN_OFF, lambda x: runs.append(1))
|
||||||
|
|
||||||
comps.turn_off(self.hass, 'light.Bowl')
|
comps.turn_off(self.hass, 'light.Bowl')
|
||||||
|
|
||||||
self.hass._pool.block_till_done()
|
self.hass._pool.block_till_done()
|
||||||
|
|
||||||
self.assertEqual(1, len(runs))
|
self.assertEqual(1, len(runs))
|
||||||
|
|
||||||
def test_extract_entity_ids(self):
|
|
||||||
""" Test extract_entity_ids method. """
|
|
||||||
call = ha.ServiceCall('light', 'turn_on',
|
|
||||||
{comps.ATTR_ENTITY_ID: 'light.Bowl'})
|
|
||||||
|
|
||||||
self.assertEqual(['light.Bowl'],
|
|
||||||
comps.extract_entity_ids(self.hass, call))
|
|
||||||
|
|
||||||
call = ha.ServiceCall('light', 'turn_on',
|
|
||||||
{comps.ATTR_ENTITY_ID: ['light.Bowl']})
|
|
||||||
|
|
||||||
self.assertEqual(['light.Bowl'],
|
|
||||||
comps.extract_entity_ids(self.hass, call))
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import unittest
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.components.demo as demo
|
import homeassistant.components.demo as demo
|
||||||
from homeassistant.components import (
|
from homeassistant.const import (
|
||||||
SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_ON, STATE_OFF, ATTR_ENTITY_ID)
|
SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_ON, STATE_OFF, ATTR_ENTITY_ID)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,11 @@ import os
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
from homeassistant.components import (
|
from homeassistant.const import (
|
||||||
STATE_HOME, STATE_NOT_HOME, ATTR_ENTITY_PICTURE)
|
STATE_HOME, STATE_NOT_HOME, ATTR_ENTITY_PICTURE, CONF_PLATFORM)
|
||||||
import homeassistant.components.device_tracker as device_tracker
|
import homeassistant.components.device_tracker as device_tracker
|
||||||
|
|
||||||
from helper import get_test_home_assistant
|
from helpers import get_test_home_assistant
|
||||||
|
|
||||||
|
|
||||||
def setUpModule(): # pylint: disable=invalid-name
|
def setUpModule(): # pylint: disable=invalid-name
|
||||||
|
@ -64,7 +64,7 @@ class TestComponentsDeviceTracker(unittest.TestCase):
|
||||||
|
|
||||||
# Test with non-existing component
|
# Test with non-existing component
|
||||||
self.assertFalse(device_tracker.setup(
|
self.assertFalse(device_tracker.setup(
|
||||||
self.hass, {device_tracker.DOMAIN: {ha.CONF_TYPE: 'nonexisting'}}
|
self.hass, {device_tracker.DOMAIN: {CONF_PLATFORM: 'nonexisting'}}
|
||||||
))
|
))
|
||||||
|
|
||||||
# Test with a bad known device file around
|
# Test with a bad known device file around
|
||||||
|
@ -72,7 +72,7 @@ class TestComponentsDeviceTracker(unittest.TestCase):
|
||||||
fil.write("bad data\nbad data\n")
|
fil.write("bad data\nbad data\n")
|
||||||
|
|
||||||
self.assertFalse(device_tracker.setup(self.hass, {
|
self.assertFalse(device_tracker.setup(self.hass, {
|
||||||
device_tracker.DOMAIN: {ha.CONF_TYPE: 'test'}
|
device_tracker.DOMAIN: {CONF_PLATFORM: 'test'}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
def test_device_tracker(self):
|
def test_device_tracker(self):
|
||||||
|
@ -84,7 +84,7 @@ class TestComponentsDeviceTracker(unittest.TestCase):
|
||||||
scanner.come_home('dev2')
|
scanner.come_home('dev2')
|
||||||
|
|
||||||
self.assertTrue(device_tracker.setup(self.hass, {
|
self.assertTrue(device_tracker.setup(self.hass, {
|
||||||
device_tracker.DOMAIN: {ha.CONF_TYPE: 'test'}
|
device_tracker.DOMAIN: {CONF_PLATFORM: 'test'}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
# Ensure a new known devices file has been created.
|
# Ensure a new known devices file has been created.
|
||||||
|
|
|
@ -9,7 +9,7 @@ import unittest
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.components as comps
|
from homeassistant.const import STATE_ON, STATE_OFF, STATE_HOME, STATE_NOT_HOME
|
||||||
import homeassistant.components.group as group
|
import homeassistant.components.group as group
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,9 +25,9 @@ class TestComponentsGroup(unittest.TestCase):
|
||||||
""" Init needed objects. """
|
""" Init needed objects. """
|
||||||
self.hass = ha.HomeAssistant()
|
self.hass = ha.HomeAssistant()
|
||||||
|
|
||||||
self.hass.states.set('light.Bowl', comps.STATE_ON)
|
self.hass.states.set('light.Bowl', STATE_ON)
|
||||||
self.hass.states.set('light.Ceiling', comps.STATE_OFF)
|
self.hass.states.set('light.Ceiling', STATE_OFF)
|
||||||
self.hass.states.set('switch.AC', comps.STATE_OFF)
|
self.hass.states.set('switch.AC', STATE_OFF)
|
||||||
group.setup_group(self.hass, 'init_group',
|
group.setup_group(self.hass, 'init_group',
|
||||||
['light.Bowl', 'light.Ceiling'], False)
|
['light.Bowl', 'light.Ceiling'], False)
|
||||||
group.setup_group(self.hass, 'mixed_group',
|
group.setup_group(self.hass, 'mixed_group',
|
||||||
|
@ -47,27 +47,27 @@ class TestComponentsGroup(unittest.TestCase):
|
||||||
self.assertIn(self.group_name, self.hass.states.entity_ids())
|
self.assertIn(self.group_name, self.hass.states.entity_ids())
|
||||||
|
|
||||||
group_state = self.hass.states.get(self.group_name)
|
group_state = self.hass.states.get(self.group_name)
|
||||||
self.assertEqual(comps.STATE_ON, group_state.state)
|
self.assertEqual(STATE_ON, group_state.state)
|
||||||
self.assertTrue(group_state.attributes[group.ATTR_AUTO])
|
self.assertTrue(group_state.attributes[group.ATTR_AUTO])
|
||||||
|
|
||||||
# Turn the Bowl off and see if group turns off
|
# Turn the Bowl off and see if group turns off
|
||||||
self.hass.states.set('light.Bowl', comps.STATE_OFF)
|
self.hass.states.set('light.Bowl', STATE_OFF)
|
||||||
|
|
||||||
self.hass._pool.block_till_done()
|
self.hass._pool.block_till_done()
|
||||||
|
|
||||||
group_state = self.hass.states.get(self.group_name)
|
group_state = self.hass.states.get(self.group_name)
|
||||||
self.assertEqual(comps.STATE_OFF, group_state.state)
|
self.assertEqual(STATE_OFF, group_state.state)
|
||||||
|
|
||||||
# Turn the Ceiling on and see if group turns on
|
# Turn the Ceiling on and see if group turns on
|
||||||
self.hass.states.set('light.Ceiling', comps.STATE_ON)
|
self.hass.states.set('light.Ceiling', STATE_ON)
|
||||||
|
|
||||||
self.hass._pool.block_till_done()
|
self.hass._pool.block_till_done()
|
||||||
|
|
||||||
group_state = self.hass.states.get(self.group_name)
|
group_state = self.hass.states.get(self.group_name)
|
||||||
self.assertEqual(comps.STATE_ON, group_state.state)
|
self.assertEqual(STATE_ON, group_state.state)
|
||||||
|
|
||||||
# Try to setup a group with mixed groupable states
|
# Try to setup a group with mixed groupable states
|
||||||
self.hass.states.set('device_tracker.Paulus', comps.STATE_HOME)
|
self.hass.states.set('device_tracker.Paulus', STATE_HOME)
|
||||||
self.assertFalse(group.setup_group(
|
self.assertFalse(group.setup_group(
|
||||||
self.hass, 'person_and_light',
|
self.hass, 'person_and_light',
|
||||||
['light.Bowl', 'device_tracker.Paulus']))
|
['light.Bowl', 'device_tracker.Paulus']))
|
||||||
|
@ -91,12 +91,12 @@ class TestComponentsGroup(unittest.TestCase):
|
||||||
|
|
||||||
def test__get_group_type(self):
|
def test__get_group_type(self):
|
||||||
""" Test _get_group_type method. """
|
""" Test _get_group_type method. """
|
||||||
self.assertEqual('on_off', group._get_group_type(comps.STATE_ON))
|
self.assertEqual('on_off', group._get_group_type(STATE_ON))
|
||||||
self.assertEqual('on_off', group._get_group_type(comps.STATE_OFF))
|
self.assertEqual('on_off', group._get_group_type(STATE_OFF))
|
||||||
self.assertEqual('home_not_home',
|
self.assertEqual('home_not_home',
|
||||||
group._get_group_type(comps.STATE_HOME))
|
group._get_group_type(STATE_HOME))
|
||||||
self.assertEqual('home_not_home',
|
self.assertEqual('home_not_home',
|
||||||
group._get_group_type(comps.STATE_NOT_HOME))
|
group._get_group_type(STATE_NOT_HOME))
|
||||||
|
|
||||||
# Unsupported state
|
# Unsupported state
|
||||||
self.assertIsNone(group._get_group_type('unsupported_state'))
|
self.assertIsNone(group._get_group_type('unsupported_state'))
|
||||||
|
@ -104,7 +104,7 @@ class TestComponentsGroup(unittest.TestCase):
|
||||||
def test_is_on(self):
|
def test_is_on(self):
|
||||||
""" Test is_on method. """
|
""" Test is_on method. """
|
||||||
self.assertTrue(group.is_on(self.hass, self.group_name))
|
self.assertTrue(group.is_on(self.hass, self.group_name))
|
||||||
self.hass.states.set('light.Bowl', comps.STATE_OFF)
|
self.hass.states.set('light.Bowl', STATE_OFF)
|
||||||
self.hass._pool.block_till_done()
|
self.hass._pool.block_till_done()
|
||||||
self.assertFalse(group.is_on(self.hass, self.group_name))
|
self.assertFalse(group.is_on(self.hass, self.group_name))
|
||||||
|
|
||||||
|
@ -159,5 +159,5 @@ class TestComponentsGroup(unittest.TestCase):
|
||||||
group_state = self.hass.states.get(
|
group_state = self.hass.states.get(
|
||||||
group.ENTITY_ID_FORMAT.format('second_group'))
|
group.ENTITY_ID_FORMAT.format('second_group'))
|
||||||
|
|
||||||
self.assertEqual(comps.STATE_ON, group_state.state)
|
self.assertEqual(STATE_ON, group_state.state)
|
||||||
self.assertFalse(group_state.attributes[group.ATTR_AUTO])
|
self.assertFalse(group_state.attributes[group.ATTR_AUTO])
|
||||||
|
|
|
@ -11,12 +11,12 @@ import os
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.components import (
|
from homeassistant.const import (
|
||||||
get_component, ATTR_ENTITY_ID, STATE_ON, STATE_OFF,
|
ATTR_ENTITY_ID, STATE_ON, STATE_OFF, CONF_TYPE,
|
||||||
SERVICE_TURN_ON, SERVICE_TURN_OFF)
|
SERVICE_TURN_ON, SERVICE_TURN_OFF)
|
||||||
import homeassistant.components.light as light
|
import homeassistant.components.light as light
|
||||||
|
|
||||||
from helper import mock_service, get_test_home_assistant
|
from helpers import mock_service, get_test_home_assistant
|
||||||
|
|
||||||
|
|
||||||
class TestLight(unittest.TestCase):
|
class TestLight(unittest.TestCase):
|
||||||
|
@ -98,11 +98,11 @@ class TestLight(unittest.TestCase):
|
||||||
|
|
||||||
def test_services(self):
|
def test_services(self):
|
||||||
""" Test the provided services. """
|
""" Test the provided services. """
|
||||||
platform = get_component('light.test')
|
platform = loader.get_component('light.test')
|
||||||
|
|
||||||
platform.init()
|
platform.init()
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
light.setup(self.hass, {light.DOMAIN: {ha.CONF_TYPE: 'test'}}))
|
light.setup(self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}))
|
||||||
|
|
||||||
dev1, dev2, dev3 = platform.get_lights(None, None)
|
dev1, dev2, dev3 = platform.get_lights(None, None)
|
||||||
|
|
||||||
|
@ -223,22 +223,22 @@ class TestLight(unittest.TestCase):
|
||||||
|
|
||||||
# Test with non-existing component
|
# Test with non-existing component
|
||||||
self.assertFalse(light.setup(
|
self.assertFalse(light.setup(
|
||||||
self.hass, {light.DOMAIN: {ha.CONF_TYPE: 'nonexisting'}}
|
self.hass, {light.DOMAIN: {CONF_TYPE: 'nonexisting'}}
|
||||||
))
|
))
|
||||||
|
|
||||||
# Test if light component returns 0 lightes
|
# Test if light component returns 0 lightes
|
||||||
platform = get_component('light.test')
|
platform = loader.get_component('light.test')
|
||||||
platform.init(True)
|
platform.init(True)
|
||||||
|
|
||||||
self.assertEqual([], platform.get_lights(None, None))
|
self.assertEqual([], platform.get_lights(None, None))
|
||||||
|
|
||||||
self.assertFalse(light.setup(
|
self.assertFalse(light.setup(
|
||||||
self.hass, {light.DOMAIN: {ha.CONF_TYPE: 'test'}}
|
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
||||||
))
|
))
|
||||||
|
|
||||||
def test_light_profiles(self):
|
def test_light_profiles(self):
|
||||||
""" Test light profiles. """
|
""" Test light profiles. """
|
||||||
platform = get_component('light.test')
|
platform = loader.get_component('light.test')
|
||||||
platform.init()
|
platform.init()
|
||||||
|
|
||||||
user_light_file = self.hass.get_config_path(light.LIGHT_PROFILES_FILE)
|
user_light_file = self.hass.get_config_path(light.LIGHT_PROFILES_FILE)
|
||||||
|
@ -249,7 +249,7 @@ class TestLight(unittest.TestCase):
|
||||||
user_file.write('I,WILL,NOT,WORK\n')
|
user_file.write('I,WILL,NOT,WORK\n')
|
||||||
|
|
||||||
self.assertFalse(light.setup(
|
self.assertFalse(light.setup(
|
||||||
self.hass, {light.DOMAIN: {ha.CONF_TYPE: 'test'}}
|
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
||||||
))
|
))
|
||||||
|
|
||||||
# Clean up broken file
|
# Clean up broken file
|
||||||
|
@ -260,7 +260,7 @@ class TestLight(unittest.TestCase):
|
||||||
user_file.write('test,.4,.6,100\n')
|
user_file.write('test,.4,.6,100\n')
|
||||||
|
|
||||||
self.assertTrue(light.setup(
|
self.assertTrue(light.setup(
|
||||||
self.hass, {light.DOMAIN: {ha.CONF_TYPE: 'test'}}
|
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
||||||
))
|
))
|
||||||
|
|
||||||
dev1, dev2, dev3 = platform.get_lights(None, None)
|
dev1, dev2, dev3 = platform.get_lights(None, None)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import datetime as dt
|
||||||
import ephem
|
import ephem
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
|
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
|
||||||
import homeassistant.components.sun as sun
|
import homeassistant.components.sun as sun
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,8 +38,8 @@ class TestSun(unittest.TestCase):
|
||||||
self.assertTrue(sun.setup(
|
self.assertTrue(sun.setup(
|
||||||
self.hass,
|
self.hass,
|
||||||
{ha.DOMAIN: {
|
{ha.DOMAIN: {
|
||||||
ha.CONF_LATITUDE: '32.87336',
|
CONF_LATITUDE: '32.87336',
|
||||||
ha.CONF_LONGITUDE: '117.22743'
|
CONF_LONGITUDE: '117.22743'
|
||||||
}}))
|
}}))
|
||||||
|
|
||||||
observer = ephem.Observer()
|
observer = ephem.Observer()
|
||||||
|
@ -76,8 +77,8 @@ class TestSun(unittest.TestCase):
|
||||||
self.assertTrue(sun.setup(
|
self.assertTrue(sun.setup(
|
||||||
self.hass,
|
self.hass,
|
||||||
{ha.DOMAIN: {
|
{ha.DOMAIN: {
|
||||||
ha.CONF_LATITUDE: '32.87336',
|
CONF_LATITUDE: '32.87336',
|
||||||
ha.CONF_LONGITUDE: '117.22743'
|
CONF_LONGITUDE: '117.22743'
|
||||||
}}))
|
}}))
|
||||||
|
|
||||||
if sun.is_on(self.hass):
|
if sun.is_on(self.hass):
|
||||||
|
@ -101,24 +102,24 @@ class TestSun(unittest.TestCase):
|
||||||
self.assertFalse(sun.setup(self.hass, {}))
|
self.assertFalse(sun.setup(self.hass, {}))
|
||||||
self.assertFalse(sun.setup(self.hass, {sun.DOMAIN: {}}))
|
self.assertFalse(sun.setup(self.hass, {sun.DOMAIN: {}}))
|
||||||
self.assertFalse(sun.setup(
|
self.assertFalse(sun.setup(
|
||||||
self.hass, {ha.DOMAIN: {ha.CONF_LATITUDE: '32.87336'}}))
|
self.hass, {ha.DOMAIN: {CONF_LATITUDE: '32.87336'}}))
|
||||||
self.assertFalse(sun.setup(
|
self.assertFalse(sun.setup(
|
||||||
self.hass, {ha.DOMAIN: {ha.CONF_LONGITUDE: '117.22743'}}))
|
self.hass, {ha.DOMAIN: {CONF_LONGITUDE: '117.22743'}}))
|
||||||
self.assertFalse(sun.setup(
|
self.assertFalse(sun.setup(
|
||||||
self.hass, {ha.DOMAIN: {ha.CONF_LATITUDE: 'hello'}}))
|
self.hass, {ha.DOMAIN: {CONF_LATITUDE: 'hello'}}))
|
||||||
self.assertFalse(sun.setup(
|
self.assertFalse(sun.setup(
|
||||||
self.hass, {ha.DOMAIN: {ha.CONF_LONGITUDE: 'how are you'}}))
|
self.hass, {ha.DOMAIN: {CONF_LONGITUDE: 'how are you'}}))
|
||||||
self.assertFalse(sun.setup(
|
self.assertFalse(sun.setup(
|
||||||
self.hass, {ha.DOMAIN: {
|
self.hass, {ha.DOMAIN: {
|
||||||
ha.CONF_LATITUDE: 'wrong', ha.CONF_LONGITUDE: '117.22743'
|
CONF_LATITUDE: 'wrong', CONF_LONGITUDE: '117.22743'
|
||||||
}}))
|
}}))
|
||||||
self.assertFalse(sun.setup(
|
self.assertFalse(sun.setup(
|
||||||
self.hass, {ha.DOMAIN: {
|
self.hass, {ha.DOMAIN: {
|
||||||
ha.CONF_LATITUDE: '32.87336', ha.CONF_LONGITUDE: 'wrong'
|
CONF_LATITUDE: '32.87336', CONF_LONGITUDE: 'wrong'
|
||||||
}}))
|
}}))
|
||||||
|
|
||||||
# Test with correct config
|
# Test with correct config
|
||||||
self.assertTrue(sun.setup(
|
self.assertTrue(sun.setup(
|
||||||
self.hass, {ha.DOMAIN: {
|
self.hass, {ha.DOMAIN: {
|
||||||
ha.CONF_LATITUDE: '32.87336', ha.CONF_LONGITUDE: '117.22743'
|
CONF_LATITUDE: '32.87336', CONF_LONGITUDE: '117.22743'
|
||||||
}}))
|
}}))
|
||||||
|
|
|
@ -9,10 +9,10 @@ import unittest
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
from homeassistant.components import get_component, STATE_ON, STATE_OFF
|
from homeassistant.const import STATE_ON, STATE_OFF, CONF_PLATFORM
|
||||||
import homeassistant.components.switch as switch
|
import homeassistant.components.switch as switch
|
||||||
|
|
||||||
from helper import get_test_home_assistant
|
from helpers import get_test_home_assistant
|
||||||
|
|
||||||
|
|
||||||
class TestSwitch(unittest.TestCase):
|
class TestSwitch(unittest.TestCase):
|
||||||
|
@ -22,11 +22,11 @@ class TestSwitch(unittest.TestCase):
|
||||||
self.hass = get_test_home_assistant()
|
self.hass = get_test_home_assistant()
|
||||||
loader.prepare(self.hass)
|
loader.prepare(self.hass)
|
||||||
|
|
||||||
platform = get_component('switch.test')
|
platform = loader.get_component('switch.test')
|
||||||
|
|
||||||
platform.init()
|
platform.init()
|
||||||
self.assertTrue(switch.setup(
|
self.assertTrue(switch.setup(
|
||||||
self.hass, {switch.DOMAIN: {ha.CONF_TYPE: 'test'}}
|
self.hass, {switch.DOMAIN: {CONF_PLATFORM: 'test'}}
|
||||||
))
|
))
|
||||||
|
|
||||||
# Switch 1 is ON, switch 2 is OFF
|
# Switch 1 is ON, switch 2 is OFF
|
||||||
|
@ -90,15 +90,27 @@ class TestSwitch(unittest.TestCase):
|
||||||
|
|
||||||
# Test with non-existing component
|
# Test with non-existing component
|
||||||
self.assertFalse(switch.setup(
|
self.assertFalse(switch.setup(
|
||||||
self.hass, {switch.DOMAIN: {ha.CONF_TYPE: 'nonexisting'}}
|
self.hass, {switch.DOMAIN: {CONF_PLATFORM: 'nonexisting'}}
|
||||||
))
|
))
|
||||||
|
|
||||||
# Test if switch component returns 0 switches
|
# Test if switch component returns 0 switches
|
||||||
get_component('switch.test').init(True)
|
test_platform = loader.get_component('switch.test')
|
||||||
|
test_platform.init(True)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[], get_component('switch.test').get_switches(None, None))
|
[], test_platform.get_switches(None, None))
|
||||||
|
|
||||||
self.assertFalse(switch.setup(
|
self.assertFalse(switch.setup(
|
||||||
self.hass, {switch.DOMAIN: {ha.CONF_TYPE: 'test'}}
|
self.hass, {switch.DOMAIN: {CONF_PLATFORM: 'test'}}
|
||||||
|
))
|
||||||
|
|
||||||
|
# Test if we can load 2 platforms
|
||||||
|
loader.set_component('switch.test2', test_platform)
|
||||||
|
test_platform.init(False)
|
||||||
|
|
||||||
|
self.assertTrue(switch.setup(
|
||||||
|
self.hass, {
|
||||||
|
switch.DOMAIN: {CONF_PLATFORM: 'test'},
|
||||||
|
'{} 2'.format(switch.DOMAIN): {CONF_PLATFORM: 'test2'},
|
||||||
|
}
|
||||||
))
|
))
|
||||||
|
|
49
ha_test/test_helpers.py
Normal file
49
ha_test/test_helpers.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
"""
|
||||||
|
ha_test.test_helpers
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Tests component helpers.
|
||||||
|
"""
|
||||||
|
# pylint: disable=protected-access,too-many-public-methods
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from helpers import get_test_home_assistant
|
||||||
|
|
||||||
|
import homeassistant as ha
|
||||||
|
import homeassistant.loader as loader
|
||||||
|
from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ENTITY_ID
|
||||||
|
from homeassistant.helpers import extract_entity_ids
|
||||||
|
|
||||||
|
|
||||||
|
class TestComponentsCore(unittest.TestCase):
|
||||||
|
""" Tests homeassistant.components module. """
|
||||||
|
|
||||||
|
def setUp(self): # pylint: disable=invalid-name
|
||||||
|
""" Init needed objects. """
|
||||||
|
self.hass = get_test_home_assistant()
|
||||||
|
loader.prepare(self.hass)
|
||||||
|
|
||||||
|
self.hass.states.set('light.Bowl', STATE_ON)
|
||||||
|
self.hass.states.set('light.Ceiling', STATE_OFF)
|
||||||
|
self.hass.states.set('light.Kitchen', STATE_OFF)
|
||||||
|
|
||||||
|
loader.get_component('group').setup_group(
|
||||||
|
self.hass, 'test', ['light.Ceiling', 'light.Kitchen'])
|
||||||
|
|
||||||
|
def tearDown(self): # pylint: disable=invalid-name
|
||||||
|
""" Stop down stuff we started. """
|
||||||
|
self.hass.stop()
|
||||||
|
|
||||||
|
def test_extract_entity_ids(self):
|
||||||
|
""" Test extract_entity_ids method. """
|
||||||
|
call = ha.ServiceCall('light', 'turn_on',
|
||||||
|
{ATTR_ENTITY_ID: 'light.Bowl'})
|
||||||
|
|
||||||
|
self.assertEqual(['light.Bowl'],
|
||||||
|
extract_entity_ids(self.hass, call))
|
||||||
|
|
||||||
|
call = ha.ServiceCall('light', 'turn_on',
|
||||||
|
{ATTR_ENTITY_ID: 'group.test'})
|
||||||
|
|
||||||
|
self.assertEqual(['light.Ceiling', 'light.Kitchen'],
|
||||||
|
extract_entity_ids(self.hass, call))
|
|
@ -10,7 +10,7 @@ import unittest
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
import homeassistant.components.http as http
|
import homeassistant.components.http as http
|
||||||
|
|
||||||
from helper import get_test_home_assistant, MockModule
|
from helpers import get_test_home_assistant, MockModule
|
||||||
|
|
||||||
|
|
||||||
class TestLoader(unittest.TestCase):
|
class TestLoader(unittest.TestCase):
|
||||||
|
|
|
@ -15,32 +15,14 @@ import re
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import functools as ft
|
import functools as ft
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
|
||||||
|
SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED,
|
||||||
|
EVENT_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL)
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
|
||||||
MATCH_ALL = '*'
|
|
||||||
|
|
||||||
DOMAIN = "homeassistant"
|
DOMAIN = "homeassistant"
|
||||||
|
|
||||||
SERVICE_HOMEASSISTANT_STOP = "stop"
|
|
||||||
|
|
||||||
EVENT_HOMEASSISTANT_START = "homeassistant_start"
|
|
||||||
EVENT_HOMEASSISTANT_STOP = "homeassistant_stop"
|
|
||||||
EVENT_STATE_CHANGED = "state_changed"
|
|
||||||
EVENT_TIME_CHANGED = "time_changed"
|
|
||||||
EVENT_CALL_SERVICE = "services.call"
|
|
||||||
|
|
||||||
ATTR_NOW = "now"
|
|
||||||
ATTR_DOMAIN = "domain"
|
|
||||||
ATTR_SERVICE = "service"
|
|
||||||
|
|
||||||
CONF_LATITUDE = "latitude"
|
|
||||||
CONF_LONGITUDE = "longitude"
|
|
||||||
CONF_TYPE = "type"
|
|
||||||
CONF_HOST = "host"
|
|
||||||
CONF_HOSTS = "hosts"
|
|
||||||
CONF_USERNAME = "username"
|
|
||||||
CONF_PASSWORD = "password"
|
|
||||||
|
|
||||||
# How often time_changed event should fire
|
# How often time_changed event should fire
|
||||||
TIMER_INTERVAL = 10 # seconds
|
TIMER_INTERVAL = 10 # seconds
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,9 @@ def from_config_dict(config, hass=None):
|
||||||
# Convert it to defaultdict so components can always have config dict
|
# Convert it to defaultdict so components can always have config dict
|
||||||
config = defaultdict(dict, config)
|
config = defaultdict(dict, config)
|
||||||
|
|
||||||
# Filter out the common config section [homeassistant]
|
# Filter out the repeating and common config section [homeassistant]
|
||||||
components = (key for key in config.keys() if key != homeassistant.DOMAIN)
|
components = (key for key in config.keys()
|
||||||
|
if ' ' not in key and key != homeassistant.DOMAIN)
|
||||||
|
|
||||||
# Setup the components
|
# Setup the components
|
||||||
if core_components.setup(hass, config):
|
if core_components.setup(hass, config):
|
||||||
|
|
|
@ -19,36 +19,10 @@ import logging
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
from homeassistant.helpers import extract_entity_ids
|
||||||
from homeassistant.loader import get_component
|
from homeassistant.loader import get_component
|
||||||
|
from homeassistant.const import (
|
||||||
# Contains one string or a list of strings, each being an entity id
|
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF)
|
||||||
ATTR_ENTITY_ID = 'entity_id'
|
|
||||||
|
|
||||||
# String with a friendly name for the entity
|
|
||||||
ATTR_FRIENDLY_NAME = "friendly_name"
|
|
||||||
|
|
||||||
# A picture to represent entity
|
|
||||||
ATTR_ENTITY_PICTURE = "entity_picture"
|
|
||||||
|
|
||||||
# The unit of measurement if applicable
|
|
||||||
ATTR_UNIT_OF_MEASUREMENT = "unit_of_measurement"
|
|
||||||
|
|
||||||
STATE_ON = 'on'
|
|
||||||
STATE_OFF = 'off'
|
|
||||||
STATE_HOME = 'home'
|
|
||||||
STATE_NOT_HOME = 'not_home'
|
|
||||||
|
|
||||||
SERVICE_TURN_ON = 'turn_on'
|
|
||||||
SERVICE_TURN_OFF = 'turn_off'
|
|
||||||
|
|
||||||
SERVICE_VOLUME_UP = "volume_up"
|
|
||||||
SERVICE_VOLUME_DOWN = "volume_down"
|
|
||||||
SERVICE_VOLUME_MUTE = "volume_mute"
|
|
||||||
SERVICE_MEDIA_PLAY_PAUSE = "media_play_pause"
|
|
||||||
SERVICE_MEDIA_PLAY = "media_play"
|
|
||||||
SERVICE_MEDIA_PAUSE = "media_pause"
|
|
||||||
SERVICE_MEDIA_NEXT_TRACK = "media_next_track"
|
|
||||||
SERVICE_MEDIA_PREV_TRACK = "media_prev_track"
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -96,79 +70,6 @@ def turn_off(hass, entity_id=None, **service_data):
|
||||||
hass.services.call(ha.DOMAIN, SERVICE_TURN_OFF, service_data)
|
hass.services.call(ha.DOMAIN, SERVICE_TURN_OFF, service_data)
|
||||||
|
|
||||||
|
|
||||||
def extract_entity_ids(hass, service):
|
|
||||||
"""
|
|
||||||
Helper method to extract a list of entity ids from a service call.
|
|
||||||
Will convert group entity ids to the entity ids it represents.
|
|
||||||
"""
|
|
||||||
entity_ids = []
|
|
||||||
|
|
||||||
if service.data and ATTR_ENTITY_ID in service.data:
|
|
||||||
group = get_component('group')
|
|
||||||
|
|
||||||
# Entity ID attr can be a list or a string
|
|
||||||
service_ent_id = service.data[ATTR_ENTITY_ID]
|
|
||||||
if isinstance(service_ent_id, list):
|
|
||||||
ent_ids = service_ent_id
|
|
||||||
else:
|
|
||||||
ent_ids = [service_ent_id]
|
|
||||||
|
|
||||||
entity_ids.extend(
|
|
||||||
ent_id for ent_id
|
|
||||||
in group.expand_entity_ids(hass, ent_ids)
|
|
||||||
if ent_id not in entity_ids)
|
|
||||||
|
|
||||||
return entity_ids
|
|
||||||
|
|
||||||
|
|
||||||
class ToggleDevice(object):
|
|
||||||
""" ABC for devices that can be turned on and off. """
|
|
||||||
# pylint: disable=no-self-use
|
|
||||||
|
|
||||||
entity_id = None
|
|
||||||
|
|
||||||
def get_name(self):
|
|
||||||
""" Returns the name of the device if any. """
|
|
||||||
return None
|
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
|
||||||
""" Turn the device on. """
|
|
||||||
pass
|
|
||||||
|
|
||||||
def turn_off(self, **kwargs):
|
|
||||||
""" Turn the device off. """
|
|
||||||
pass
|
|
||||||
|
|
||||||
def is_on(self):
|
|
||||||
""" True if device is on. """
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_state_attributes(self):
|
|
||||||
""" Returns optional state attributes. """
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
""" Retrieve latest state from the real device. """
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_ha_state(self, hass, force_refresh=False):
|
|
||||||
"""
|
|
||||||
Updates Home Assistant with current state of device.
|
|
||||||
If force_refresh == True will update device before setting state.
|
|
||||||
"""
|
|
||||||
if self.entity_id is None:
|
|
||||||
raise ha.NoEntitySpecifiedError(
|
|
||||||
"No entity specified for device {}".format(self.get_name()))
|
|
||||||
|
|
||||||
if force_refresh:
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
state = STATE_ON if self.is_on() else STATE_OFF
|
|
||||||
|
|
||||||
return hass.states.set(self.entity_id, state,
|
|
||||||
self.get_state_attributes())
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup general services related to homeassistant. """
|
""" Setup general services related to homeassistant. """
|
||||||
|
|
|
@ -6,9 +6,14 @@ Provides functionality to interact with Chromecasts.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant as ha
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
import homeassistant.components as components
|
from homeassistant.helpers import extract_entity_ids
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, SERVICE_TURN_OFF, SERVICE_VOLUME_UP,
|
||||||
|
SERVICE_VOLUME_DOWN, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY,
|
||||||
|
SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREV_TRACK,
|
||||||
|
CONF_HOSTS)
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = 'chromecast'
|
DOMAIN = 'chromecast'
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
@ -46,58 +51,58 @@ def is_on(hass, entity_id=None):
|
||||||
|
|
||||||
def turn_off(hass, entity_id=None):
|
def turn_off(hass, entity_id=None):
|
||||||
""" Will turn off specified Chromecast or all. """
|
""" Will turn off specified Chromecast or all. """
|
||||||
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
||||||
|
|
||||||
hass.services.call(DOMAIN, components.SERVICE_TURN_OFF, data)
|
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
|
||||||
|
|
||||||
|
|
||||||
def volume_up(hass, entity_id=None):
|
def volume_up(hass, entity_id=None):
|
||||||
""" Send the chromecast the command for volume up. """
|
""" Send the chromecast the command for volume up. """
|
||||||
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
||||||
|
|
||||||
hass.services.call(DOMAIN, components.SERVICE_VOLUME_UP, data)
|
hass.services.call(DOMAIN, SERVICE_VOLUME_UP, data)
|
||||||
|
|
||||||
|
|
||||||
def volume_down(hass, entity_id=None):
|
def volume_down(hass, entity_id=None):
|
||||||
""" Send the chromecast the command for volume down. """
|
""" Send the chromecast the command for volume down. """
|
||||||
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
||||||
|
|
||||||
hass.services.call(DOMAIN, components.SERVICE_VOLUME_DOWN, data)
|
hass.services.call(DOMAIN, SERVICE_VOLUME_DOWN, data)
|
||||||
|
|
||||||
|
|
||||||
def media_play_pause(hass, entity_id=None):
|
def media_play_pause(hass, entity_id=None):
|
||||||
""" Send the chromecast the command for play/pause. """
|
""" Send the chromecast the command for play/pause. """
|
||||||
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
||||||
|
|
||||||
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PLAY_PAUSE, data)
|
hass.services.call(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE, data)
|
||||||
|
|
||||||
|
|
||||||
def media_play(hass, entity_id=None):
|
def media_play(hass, entity_id=None):
|
||||||
""" Send the chromecast the command for play/pause. """
|
""" Send the chromecast the command for play/pause. """
|
||||||
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
||||||
|
|
||||||
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PLAY, data)
|
hass.services.call(DOMAIN, SERVICE_MEDIA_PLAY, data)
|
||||||
|
|
||||||
|
|
||||||
def media_pause(hass, entity_id=None):
|
def media_pause(hass, entity_id=None):
|
||||||
""" Send the chromecast the command for play/pause. """
|
""" Send the chromecast the command for play/pause. """
|
||||||
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
||||||
|
|
||||||
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PAUSE, data)
|
hass.services.call(DOMAIN, SERVICE_MEDIA_PAUSE, data)
|
||||||
|
|
||||||
|
|
||||||
def media_next_track(hass, entity_id=None):
|
def media_next_track(hass, entity_id=None):
|
||||||
""" Send the chromecast the command for next track. """
|
""" Send the chromecast the command for next track. """
|
||||||
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
||||||
|
|
||||||
hass.services.call(DOMAIN, components.SERVICE_MEDIA_NEXT_TRACK, data)
|
hass.services.call(DOMAIN, SERVICE_MEDIA_NEXT_TRACK, data)
|
||||||
|
|
||||||
|
|
||||||
def media_prev_track(hass, entity_id=None):
|
def media_prev_track(hass, entity_id=None):
|
||||||
""" Send the chromecast the command for prev track. """
|
""" Send the chromecast the command for prev track. """
|
||||||
data = {components.ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
||||||
|
|
||||||
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PREV_TRACK, data)
|
hass.services.call(DOMAIN, SERVICE_MEDIA_PREV_TRACK, data)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-locals, too-many-branches
|
# pylint: disable=too-many-locals, too-many-branches
|
||||||
|
@ -114,8 +119,8 @@ def setup(hass, config):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if ha.CONF_HOSTS in config[DOMAIN]:
|
if CONF_HOSTS in config[DOMAIN]:
|
||||||
hosts = config[DOMAIN][ha.CONF_HOSTS].split(",")
|
hosts = config[DOMAIN][CONF_HOSTS].split(",")
|
||||||
|
|
||||||
# If no hosts given, scan for chromecasts
|
# If no hosts given, scan for chromecasts
|
||||||
else:
|
else:
|
||||||
|
@ -131,7 +136,7 @@ def setup(hass, config):
|
||||||
entity_id = util.ensure_unique_string(
|
entity_id = util.ensure_unique_string(
|
||||||
ENTITY_ID_FORMAT.format(
|
ENTITY_ID_FORMAT.format(
|
||||||
util.slugify(cast.device.friendly_name)),
|
util.slugify(cast.device.friendly_name)),
|
||||||
list(casts.keys()))
|
casts.keys())
|
||||||
|
|
||||||
casts[entity_id] = cast
|
casts[entity_id] = cast
|
||||||
|
|
||||||
|
@ -148,7 +153,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
status = chromecast.app
|
status = chromecast.app
|
||||||
|
|
||||||
state_attr = {components.ATTR_FRIENDLY_NAME:
|
state_attr = {ATTR_FRIENDLY_NAME:
|
||||||
chromecast.device.friendly_name}
|
chromecast.device.friendly_name}
|
||||||
|
|
||||||
if status and status.app_id != pychromecast.APP_ID['HOME']:
|
if status and status.app_id != pychromecast.APP_ID['HOME']:
|
||||||
|
@ -196,7 +201,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
def _service_to_entities(service):
|
def _service_to_entities(service):
|
||||||
""" Helper method to get entities from service. """
|
""" Helper method to get entities from service. """
|
||||||
entity_ids = components.extract_entity_ids(hass, service)
|
entity_ids = extract_entity_ids(hass, service)
|
||||||
|
|
||||||
if entity_ids:
|
if entity_ids:
|
||||||
for entity_id in entity_ids:
|
for entity_id in entity_ids:
|
||||||
|
@ -274,25 +279,25 @@ def setup(hass, config):
|
||||||
|
|
||||||
hass.track_time_change(update_chromecast_states)
|
hass.track_time_change(update_chromecast_states)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_TURN_OFF,
|
hass.services.register(DOMAIN, SERVICE_TURN_OFF,
|
||||||
turn_off_service)
|
turn_off_service)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_VOLUME_UP,
|
hass.services.register(DOMAIN, SERVICE_VOLUME_UP,
|
||||||
volume_up_service)
|
volume_up_service)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_VOLUME_DOWN,
|
hass.services.register(DOMAIN, SERVICE_VOLUME_DOWN,
|
||||||
volume_down_service)
|
volume_down_service)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_MEDIA_PLAY_PAUSE,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE,
|
||||||
media_play_pause_service)
|
media_play_pause_service)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_MEDIA_PLAY,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY,
|
||||||
media_play_service)
|
media_play_service)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_MEDIA_PAUSE,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_PAUSE,
|
||||||
media_pause_service)
|
media_pause_service)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_MEDIA_NEXT_TRACK,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_NEXT_TRACK,
|
||||||
media_next_track_service)
|
media_next_track_service)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, "start_fireplace",
|
hass.services.register(DOMAIN, "start_fireplace",
|
||||||
|
|
|
@ -8,9 +8,10 @@ import random
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
from homeassistant.components import (
|
from homeassistant.helpers import extract_entity_ids
|
||||||
|
from homeassistant.const import (
|
||||||
SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_ON, STATE_OFF,
|
SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_ON, STATE_OFF,
|
||||||
ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID, extract_entity_ids)
|
ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID, CONF_LATITUDE, CONF_LONGITUDE)
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_XY_COLOR, ATTR_BRIGHTNESS, GROUP_NAME_ALL_LIGHTS)
|
ATTR_XY_COLOR, ATTR_BRIGHTNESS, GROUP_NAME_ALL_LIGHTS)
|
||||||
from homeassistant.util import split_entity_id
|
from homeassistant.util import split_entity_id
|
||||||
|
@ -65,11 +66,11 @@ def setup(hass, config):
|
||||||
hass.states.set(entity_id, STATE_OFF)
|
hass.states.set(entity_id, STATE_OFF)
|
||||||
|
|
||||||
# Setup sun
|
# Setup sun
|
||||||
if ha.CONF_LATITUDE not in config[ha.DOMAIN]:
|
if CONF_LATITUDE not in config[ha.DOMAIN]:
|
||||||
config[ha.DOMAIN][ha.CONF_LATITUDE] = '32.87336'
|
config[ha.DOMAIN][CONF_LATITUDE] = '32.87336'
|
||||||
|
|
||||||
if ha.CONF_LONGITUDE not in config[ha.DOMAIN]:
|
if CONF_LONGITUDE not in config[ha.DOMAIN]:
|
||||||
config[ha.DOMAIN][ha.CONF_LONGITUDE] = '-117.22743'
|
config[ha.DOMAIN][CONF_LONGITUDE] = '-117.22743'
|
||||||
|
|
||||||
loader.get_component('sun').setup(hass, config)
|
loader.get_component('sun').setup(hass, config)
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ the state of the sun and devices.
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import homeassistant.components as components
|
from homeassistant.const import STATE_HOME, STATE_NOT_HOME
|
||||||
from . import light, sun, device_tracker, group
|
from . import light, sun, device_tracker, group
|
||||||
|
|
||||||
DOMAIN = "device_sun_light_trigger"
|
DOMAIN = "device_sun_light_trigger"
|
||||||
|
@ -108,7 +108,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
# Specific device came home ?
|
# Specific device came home ?
|
||||||
if entity != device_tracker.ENTITY_ID_ALL_DEVICES and \
|
if entity != device_tracker.ENTITY_ID_ALL_DEVICES and \
|
||||||
new_state.state == components.STATE_HOME:
|
new_state.state == STATE_HOME:
|
||||||
|
|
||||||
# These variables are needed for the elif check
|
# These variables are needed for the elif check
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
|
@ -143,7 +143,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
# Did all devices leave the house?
|
# Did all devices leave the house?
|
||||||
elif (entity == device_tracker.ENTITY_ID_ALL_DEVICES and
|
elif (entity == device_tracker.ENTITY_ID_ALL_DEVICES and
|
||||||
new_state.state == components.STATE_NOT_HOME and lights_are_on
|
new_state.state == STATE_NOT_HOME and lights_are_on
|
||||||
and not disable_turn_off):
|
and not disable_turn_off):
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
|
@ -154,12 +154,12 @@ def setup(hass, config):
|
||||||
# Track home coming of each device
|
# Track home coming of each device
|
||||||
hass.states.track_change(
|
hass.states.track_change(
|
||||||
device_entity_ids, check_light_on_dev_state_change,
|
device_entity_ids, check_light_on_dev_state_change,
|
||||||
components.STATE_NOT_HOME, components.STATE_HOME)
|
STATE_NOT_HOME, STATE_HOME)
|
||||||
|
|
||||||
# Track when all devices are gone to shut down lights
|
# Track when all devices are gone to shut down lights
|
||||||
hass.states.track_change(
|
hass.states.track_change(
|
||||||
device_tracker.ENTITY_ID_ALL_DEVICES,
|
device_tracker.ENTITY_ID_ALL_DEVICES,
|
||||||
check_light_on_dev_state_change,
|
check_light_on_dev_state_change,
|
||||||
components.STATE_HOME, components.STATE_NOT_HOME)
|
STATE_HOME, STATE_NOT_HOME)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -10,12 +10,14 @@ import os
|
||||||
import csv
|
import csv
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import homeassistant as ha
|
|
||||||
from homeassistant.loader import get_component
|
from homeassistant.loader import get_component
|
||||||
|
from homeassistant.helpers import validate_config
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
|
||||||
from homeassistant.components import (
|
from homeassistant.const import (
|
||||||
group, STATE_HOME, STATE_NOT_HOME, ATTR_ENTITY_PICTURE, ATTR_FRIENDLY_NAME)
|
STATE_HOME, STATE_NOT_HOME, ATTR_ENTITY_PICTURE, ATTR_FRIENDLY_NAME,
|
||||||
|
CONF_PLATFORM, CONF_TYPE)
|
||||||
|
from homeassistant.components import group
|
||||||
|
|
||||||
DOMAIN = "device_tracker"
|
DOMAIN = "device_tracker"
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
@ -49,10 +51,20 @@ def is_on(hass, entity_id=None):
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Sets up the device tracker. """
|
""" Sets up the device tracker. """
|
||||||
|
|
||||||
if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, _LOGGER):
|
# CONF_TYPE is deprecated for CONF_PLATOFRM. We keep supporting it for now.
|
||||||
|
if not (validate_config(config, {DOMAIN: [CONF_PLATFORM]}, _LOGGER)
|
||||||
|
or validate_config(config, {DOMAIN: [CONF_TYPE]}, _LOGGER)):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
tracker_type = config[DOMAIN][ha.CONF_TYPE]
|
tracker_type = config[DOMAIN].get(CONF_PLATFORM)
|
||||||
|
|
||||||
|
if tracker_type is None:
|
||||||
|
tracker_type = config[DOMAIN][CONF_TYPE]
|
||||||
|
|
||||||
|
_LOGGER.warning((
|
||||||
|
"Please update your config for %s to use 'platform' "
|
||||||
|
"instead of 'type'"), tracker_type)
|
||||||
|
|
||||||
tracker_implementation = get_component(
|
tracker_implementation = get_component(
|
||||||
'device_tracker.{}'.format(tracker_type))
|
'device_tracker.{}'.format(tracker_type))
|
||||||
|
|
|
@ -6,8 +6,9 @@ import re
|
||||||
import threading
|
import threading
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
import homeassistant as ha
|
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
|
||||||
import homeassistant.util as util
|
from homeassistant.helpers import validate_config
|
||||||
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
from homeassistant.components.device_tracker import DOMAIN
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
|
@ -19,10 +20,9 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Luci scanner. """
|
""" Validates config and returns a Luci scanner. """
|
||||||
if not util.validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [ha.CONF_HOST, ha.CONF_USERNAME,
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
ha.CONF_PASSWORD]},
|
_LOGGER):
|
||||||
_LOGGER):
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
scanner = LuciDeviceScanner(config[DOMAIN])
|
scanner = LuciDeviceScanner(config[DOMAIN])
|
||||||
|
@ -45,8 +45,8 @@ class LuciDeviceScanner(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
host = config[ha.CONF_HOST]
|
host = config[CONF_HOST]
|
||||||
username, password = config[ha.CONF_USERNAME], config[ha.CONF_PASSWORD]
|
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
||||||
|
|
||||||
self.parse_api_pattern = re.compile(r"(?P<param>\w*) = (?P<value>.*);")
|
self.parse_api_pattern = re.compile(r"(?P<param>\w*) = (?P<value>.*);")
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ class LuciDeviceScanner(object):
|
||||||
return
|
return
|
||||||
return self.mac2name.get(device, None)
|
return self.mac2name.get(device, None)
|
||||||
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
""" Ensures the information from the Luci router is up to date.
|
""" Ensures the information from the Luci router is up to date.
|
||||||
Returns boolean if scanning successful. """
|
Returns boolean if scanning successful. """
|
||||||
|
|
|
@ -3,8 +3,9 @@ import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import homeassistant as ha
|
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
|
||||||
import homeassistant.util as util
|
from homeassistant.helpers import validate_config
|
||||||
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
from homeassistant.components.device_tracker import DOMAIN
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
|
@ -16,10 +17,9 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Netgear scanner. """
|
""" Validates config and returns a Netgear scanner. """
|
||||||
if not util.validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [ha.CONF_HOST, ha.CONF_USERNAME,
|
{DOMAIN: [CONF_HOST, CONF_USERNAME, CONF_PASSWORD]},
|
||||||
ha.CONF_PASSWORD]},
|
_LOGGER):
|
||||||
_LOGGER):
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
scanner = NetgearDeviceScanner(config[DOMAIN])
|
scanner = NetgearDeviceScanner(config[DOMAIN])
|
||||||
|
@ -31,8 +31,8 @@ class NetgearDeviceScanner(object):
|
||||||
""" This class queries a Netgear wireless router using the SOAP-api. """
|
""" This class queries a Netgear wireless router using the SOAP-api. """
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
host = config[ha.CONF_HOST]
|
host = config[CONF_HOST]
|
||||||
username, password = config[ha.CONF_USERNAME], config[ha.CONF_PASSWORD]
|
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
||||||
|
|
||||||
self.last_results = []
|
self.last_results = []
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ class NetgearDeviceScanner(object):
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_info(self):
|
def _update_info(self):
|
||||||
""" Retrieves latest information from the Netgear router.
|
""" Retrieves latest information from the Netgear router.
|
||||||
Returns boolean if scanning successful. """
|
Returns boolean if scanning successful. """
|
||||||
|
|
|
@ -7,8 +7,9 @@ import threading
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
import homeassistant as ha
|
from homeassistant.const import CONF_HOST, CONF_USERNAME, CONF_PASSWORD
|
||||||
import homeassistant.util as util
|
from homeassistant.helpers import validate_config
|
||||||
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.components.device_tracker import DOMAIN
|
from homeassistant.components.device_tracker import DOMAIN
|
||||||
|
|
||||||
# Return cached results if last scan was less then this time ago
|
# Return cached results if last scan was less then this time ago
|
||||||
|
@ -22,10 +23,10 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Tomato scanner. """
|
""" Validates config and returns a Tomato scanner. """
|
||||||
if not util.validate_config(config,
|
if not validate_config(config,
|
||||||
{DOMAIN: [ha.CONF_HOST, ha.CONF_USERNAME,
|
{DOMAIN: [CONF_HOST, CONF_USERNAME,
|
||||||
ha.CONF_PASSWORD, CONF_HTTP_ID]},
|
CONF_PASSWORD, CONF_HTTP_ID]},
|
||||||
_LOGGER):
|
_LOGGER):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return TomatoDeviceScanner(config[DOMAIN])
|
return TomatoDeviceScanner(config[DOMAIN])
|
||||||
|
@ -40,8 +41,8 @@ class TomatoDeviceScanner(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
host, http_id = config[ha.CONF_HOST], config[CONF_HTTP_ID]
|
host, http_id = config[CONF_HOST], config[CONF_HTTP_ID]
|
||||||
username, password = config[ha.CONF_USERNAME], config[ha.CONF_PASSWORD]
|
username, password = config[CONF_USERNAME], config[CONF_PASSWORD]
|
||||||
|
|
||||||
self.req = requests.Request('POST',
|
self.req = requests.Request('POST',
|
||||||
'http://{}/update.cgi'.format(host),
|
'http://{}/update.cgi'.format(host),
|
||||||
|
@ -78,7 +79,7 @@ class TomatoDeviceScanner(object):
|
||||||
else:
|
else:
|
||||||
return filter_named[0]
|
return filter_named[0]
|
||||||
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
@Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def _update_tomato_info(self):
|
def _update_tomato_info(self):
|
||||||
""" Ensures the information from the Tomato router is up to date.
|
""" Ensures the information from the Tomato router is up to date.
|
||||||
Returns boolean if scanning successful. """
|
Returns boolean if scanning successful. """
|
||||||
|
|
|
@ -9,7 +9,8 @@ import logging
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import homeassistant.util as util
|
from homeassistant.helpers import validate_config
|
||||||
|
from homeassistant.util import sanitize_filename
|
||||||
|
|
||||||
DOMAIN = "downloader"
|
DOMAIN = "downloader"
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
@ -36,7 +37,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not util.validate_config(config, {DOMAIN: [CONF_DOWNLOAD_DIR]}, logger):
|
if not validate_config(config, {DOMAIN: [CONF_DOWNLOAD_DIR]}, logger):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
download_path = config[DOMAIN][CONF_DOWNLOAD_DIR]
|
download_path = config[DOMAIN][CONF_DOWNLOAD_DIR]
|
||||||
|
@ -64,7 +65,7 @@ def setup(hass, config):
|
||||||
subdir = service.data.get(ATTR_SUBDIR)
|
subdir = service.data.get(ATTR_SUBDIR)
|
||||||
|
|
||||||
if subdir:
|
if subdir:
|
||||||
subdir = util.sanitize_filename(subdir)
|
subdir = sanitize_filename(subdir)
|
||||||
|
|
||||||
final_path = None
|
final_path = None
|
||||||
|
|
||||||
|
@ -88,7 +89,7 @@ def setup(hass, config):
|
||||||
filename = "ha_download"
|
filename = "ha_download"
|
||||||
|
|
||||||
# Remove stuff to ruin paths
|
# Remove stuff to ruin paths
|
||||||
filename = util.sanitize_filename(filename)
|
filename = sanitize_filename(filename)
|
||||||
|
|
||||||
# Do we want to download to subdir, create if needed
|
# Do we want to download to subdir, create if needed
|
||||||
if subdir:
|
if subdir:
|
||||||
|
|
|
@ -9,9 +9,8 @@ import logging
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.components import (STATE_ON, STATE_OFF,
|
from homeassistant.const import (
|
||||||
STATE_HOME, STATE_NOT_HOME,
|
ATTR_ENTITY_ID, STATE_ON, STATE_OFF, STATE_HOME, STATE_NOT_HOME)
|
||||||
ATTR_ENTITY_ID)
|
|
||||||
|
|
||||||
DOMAIN = "group"
|
DOMAIN = "group"
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
|
|
@ -83,6 +83,10 @@ from socketserver import ThreadingMixIn
|
||||||
from urllib.parse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
|
from homeassistant.const import (
|
||||||
|
SERVER_PORT, URL_API, URL_API_STATES, URL_API_EVENTS, URL_API_SERVICES,
|
||||||
|
URL_API_EVENT_FORWARD, URL_API_STATES_ENTITY, AUTH_HEADER)
|
||||||
|
from homeassistant.helpers import validate_config
|
||||||
import homeassistant.remote as rem
|
import homeassistant.remote as rem
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from . import frontend
|
from . import frontend
|
||||||
|
@ -116,8 +120,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Sets up the HTTP API and debug interface. """
|
""" Sets up the HTTP API and debug interface. """
|
||||||
|
|
||||||
if not util.validate_config(config, {DOMAIN: [CONF_API_PASSWORD]},
|
if not validate_config(config, {DOMAIN: [CONF_API_PASSWORD]}, _LOGGER):
|
||||||
_LOGGER):
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
api_password = config[DOMAIN][CONF_API_PASSWORD]
|
api_password = config[DOMAIN][CONF_API_PASSWORD]
|
||||||
|
@ -125,7 +128,7 @@ def setup(hass, config):
|
||||||
# If no server host is given, accept all incoming requests
|
# If no server host is given, accept all incoming requests
|
||||||
server_host = config[DOMAIN].get(CONF_SERVER_HOST, '0.0.0.0')
|
server_host = config[DOMAIN].get(CONF_SERVER_HOST, '0.0.0.0')
|
||||||
|
|
||||||
server_port = config[DOMAIN].get(CONF_SERVER_PORT, rem.SERVER_PORT)
|
server_port = config[DOMAIN].get(CONF_SERVER_PORT, SERVER_PORT)
|
||||||
|
|
||||||
development = config[DOMAIN].get(CONF_DEVELOPMENT, "") == "1"
|
development = config[DOMAIN].get(CONF_DEVELOPMENT, "") == "1"
|
||||||
|
|
||||||
|
@ -196,10 +199,10 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
||||||
('GET', URL_ROOT, '_handle_get_root'),
|
('GET', URL_ROOT, '_handle_get_root'),
|
||||||
|
|
||||||
# /api - for validation purposes
|
# /api - for validation purposes
|
||||||
('GET', rem.URL_API, '_handle_get_api'),
|
('GET', URL_API, '_handle_get_api'),
|
||||||
|
|
||||||
# /states
|
# /states
|
||||||
('GET', rem.URL_API_STATES, '_handle_get_api_states'),
|
('GET', URL_API_STATES, '_handle_get_api_states'),
|
||||||
('GET',
|
('GET',
|
||||||
re.compile(r'/api/states/(?P<entity_id>[a-zA-Z\._0-9]+)'),
|
re.compile(r'/api/states/(?P<entity_id>[a-zA-Z\._0-9]+)'),
|
||||||
'_handle_get_api_states_entity'),
|
'_handle_get_api_states_entity'),
|
||||||
|
@ -211,13 +214,13 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
||||||
'_handle_post_state_entity'),
|
'_handle_post_state_entity'),
|
||||||
|
|
||||||
# /events
|
# /events
|
||||||
('GET', rem.URL_API_EVENTS, '_handle_get_api_events'),
|
('GET', URL_API_EVENTS, '_handle_get_api_events'),
|
||||||
('POST',
|
('POST',
|
||||||
re.compile(r'/api/events/(?P<event_type>[a-zA-Z\._0-9]+)'),
|
re.compile(r'/api/events/(?P<event_type>[a-zA-Z\._0-9]+)'),
|
||||||
'_handle_api_post_events_event'),
|
'_handle_api_post_events_event'),
|
||||||
|
|
||||||
# /services
|
# /services
|
||||||
('GET', rem.URL_API_SERVICES, '_handle_get_api_services'),
|
('GET', URL_API_SERVICES, '_handle_get_api_services'),
|
||||||
('POST',
|
('POST',
|
||||||
re.compile((r'/api/services/'
|
re.compile((r'/api/services/'
|
||||||
r'(?P<domain>[a-zA-Z\._0-9]+)/'
|
r'(?P<domain>[a-zA-Z\._0-9]+)/'
|
||||||
|
@ -225,8 +228,8 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
||||||
'_handle_post_api_services_domain_service'),
|
'_handle_post_api_services_domain_service'),
|
||||||
|
|
||||||
# /event_forwarding
|
# /event_forwarding
|
||||||
('POST', rem.URL_API_EVENT_FORWARD, '_handle_post_api_event_forward'),
|
('POST', URL_API_EVENT_FORWARD, '_handle_post_api_event_forward'),
|
||||||
('DELETE', rem.URL_API_EVENT_FORWARD,
|
('DELETE', URL_API_EVENT_FORWARD,
|
||||||
'_handle_delete_api_event_forward'),
|
'_handle_delete_api_event_forward'),
|
||||||
|
|
||||||
# Static files
|
# Static files
|
||||||
|
@ -270,7 +273,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
||||||
"Error parsing JSON", HTTP_UNPROCESSABLE_ENTITY)
|
"Error parsing JSON", HTTP_UNPROCESSABLE_ENTITY)
|
||||||
return
|
return
|
||||||
|
|
||||||
api_password = self.headers.get(rem.AUTH_HEADER)
|
api_password = self.headers.get(AUTH_HEADER)
|
||||||
|
|
||||||
if not api_password and DATA_API_PASSWORD in data:
|
if not api_password and DATA_API_PASSWORD in data:
|
||||||
api_password = data[DATA_API_PASSWORD]
|
api_password = data[DATA_API_PASSWORD]
|
||||||
|
@ -427,7 +430,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
||||||
self._write_json(
|
self._write_json(
|
||||||
state.as_dict(),
|
state.as_dict(),
|
||||||
status_code=status_code,
|
status_code=status_code,
|
||||||
location=rem.URL_API_STATES_ENTITY.format(entity_id))
|
location=URL_API_STATES_ENTITY.format(entity_id))
|
||||||
|
|
||||||
def _handle_get_api_events(self, path_match, data):
|
def _handle_get_api_events(self, path_match, data):
|
||||||
""" Handles getting overview of event listeners. """
|
""" Handles getting overview of event listeners. """
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
"""
|
"""
|
||||||
homeassistant.components.keyboard
|
homeassistant.keyboard
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Provides functionality to emulate keyboard presses on host machine.
|
Provides functionality to emulate keyboard presses on host machine.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant.components as components
|
from homeassistant.const import (
|
||||||
|
SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_VOLUME_MUTE,
|
||||||
|
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREV_TRACK,
|
||||||
|
SERVICE_MEDIA_PLAY_PAUSE)
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = "keyboard"
|
DOMAIN = "keyboard"
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
@ -14,32 +18,32 @@ DEPENDENCIES = []
|
||||||
|
|
||||||
def volume_up(hass):
|
def volume_up(hass):
|
||||||
""" Press the keyboard button for volume up. """
|
""" Press the keyboard button for volume up. """
|
||||||
hass.services.call(DOMAIN, components.SERVICE_VOLUME_UP)
|
hass.services.call(DOMAIN, SERVICE_VOLUME_UP)
|
||||||
|
|
||||||
|
|
||||||
def volume_down(hass):
|
def volume_down(hass):
|
||||||
""" Press the keyboard button for volume down. """
|
""" Press the keyboard button for volume down. """
|
||||||
hass.services.call(DOMAIN, components.SERVICE_VOLUME_DOWN)
|
hass.services.call(DOMAIN, SERVICE_VOLUME_DOWN)
|
||||||
|
|
||||||
|
|
||||||
def volume_mute(hass):
|
def volume_mute(hass):
|
||||||
""" Press the keyboard button for muting volume. """
|
""" Press the keyboard button for muting volume. """
|
||||||
hass.services.call(DOMAIN, components.SERVICE_VOLUME_MUTE)
|
hass.services.call(DOMAIN, SERVICE_VOLUME_MUTE)
|
||||||
|
|
||||||
|
|
||||||
def media_play_pause(hass):
|
def media_play_pause(hass):
|
||||||
""" Press the keyboard button for play/pause. """
|
""" Press the keyboard button for play/pause. """
|
||||||
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PLAY_PAUSE)
|
hass.services.call(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE)
|
||||||
|
|
||||||
|
|
||||||
def media_next_track(hass):
|
def media_next_track(hass):
|
||||||
""" Press the keyboard button for next track. """
|
""" Press the keyboard button for next track. """
|
||||||
hass.services.call(DOMAIN, components.SERVICE_MEDIA_NEXT_TRACK)
|
hass.services.call(DOMAIN, SERVICE_MEDIA_NEXT_TRACK)
|
||||||
|
|
||||||
|
|
||||||
def media_prev_track(hass):
|
def media_prev_track(hass):
|
||||||
""" Press the keyboard button for prev track. """
|
""" Press the keyboard button for prev track. """
|
||||||
hass.services.call(DOMAIN, components.SERVICE_MEDIA_PREV_TRACK)
|
hass.services.call(DOMAIN, SERVICE_MEDIA_PREV_TRACK)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
|
@ -56,27 +60,27 @@ def setup(hass, config):
|
||||||
keyboard = pykeyboard.PyKeyboard()
|
keyboard = pykeyboard.PyKeyboard()
|
||||||
keyboard.special_key_assignment()
|
keyboard.special_key_assignment()
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_VOLUME_UP,
|
hass.services.register(DOMAIN, SERVICE_VOLUME_UP,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.volume_up_key))
|
keyboard.tap_key(keyboard.volume_up_key))
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_VOLUME_DOWN,
|
hass.services.register(DOMAIN, SERVICE_VOLUME_DOWN,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.volume_down_key))
|
keyboard.tap_key(keyboard.volume_down_key))
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_VOLUME_MUTE,
|
hass.services.register(DOMAIN, SERVICE_VOLUME_MUTE,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.volume_mute_key))
|
keyboard.tap_key(keyboard.volume_mute_key))
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_MEDIA_PLAY_PAUSE,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.media_play_pause_key))
|
keyboard.tap_key(keyboard.media_play_pause_key))
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_MEDIA_NEXT_TRACK,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_NEXT_TRACK,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.media_next_track_key))
|
keyboard.tap_key(keyboard.media_next_track_key))
|
||||||
|
|
||||||
hass.services.register(DOMAIN, components.SERVICE_MEDIA_PREV_TRACK,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_PREV_TRACK,
|
||||||
lambda service:
|
lambda service:
|
||||||
keyboard.tap_key(keyboard.media_prev_track_key))
|
keyboard.tap_key(keyboard.media_prev_track_key))
|
||||||
|
|
||||||
|
|
|
@ -52,12 +52,12 @@ import logging
|
||||||
import os
|
import os
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
import homeassistant as ha
|
|
||||||
from homeassistant.loader import get_component
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.components import (
|
from homeassistant.const import (
|
||||||
group, extract_entity_ids, STATE_ON,
|
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
||||||
SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
from homeassistant.helpers import (
|
||||||
|
extract_entity_ids, platform_devices_from_config)
|
||||||
|
from homeassistant.components import group
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = "light"
|
DOMAIN = "light"
|
||||||
|
@ -138,9 +138,6 @@ def turn_off(hass, entity_id=None, transition=None):
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Exposes light control via statemachine and services. """
|
""" Exposes light control via statemachine and services. """
|
||||||
|
|
||||||
if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, _LOGGER):
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Load built-in profiles and custom profiles
|
# Load built-in profiles and custom profiles
|
||||||
profile_paths = [os.path.join(os.path.dirname(__file__),
|
profile_paths = [os.path.join(os.path.dirname(__file__),
|
||||||
LIGHT_PROFILES_FILE),
|
LIGHT_PROFILES_FILE),
|
||||||
|
@ -169,20 +166,9 @@ def setup(hass, config):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Load platform
|
lights = platform_devices_from_config(config, DOMAIN, hass, _LOGGER)
|
||||||
light_type = config[DOMAIN][ha.CONF_TYPE]
|
|
||||||
|
|
||||||
light_init = get_component('light.{}'.format(light_type))
|
if not lights:
|
||||||
|
|
||||||
if light_init is None:
|
|
||||||
_LOGGER.error("Unknown light type specified: %s", light_type)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
lights = light_init.get_lights(hass, config[DOMAIN])
|
|
||||||
|
|
||||||
if len(lights) == 0:
|
|
||||||
_LOGGER.error("No lights found")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
ent_to_light = {}
|
ent_to_light = {}
|
||||||
|
@ -198,7 +184,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
entity_id = util.ensure_unique_string(
|
entity_id = util.ensure_unique_string(
|
||||||
ENTITY_ID_FORMAT.format(util.slugify(name)),
|
ENTITY_ID_FORMAT.format(util.slugify(name)),
|
||||||
list(ent_to_light.keys()))
|
ent_to_light.keys())
|
||||||
|
|
||||||
light.entity_id = entity_id
|
light.entity_id = entity_id
|
||||||
ent_to_light[entity_id] = light
|
ent_to_light[entity_id] = light
|
||||||
|
|
|
@ -3,9 +3,9 @@ import logging
|
||||||
import socket
|
import socket
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import homeassistant as ha
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.components import ToggleDevice, ATTR_FRIENDLY_NAME
|
from homeassistant.helpers import ToggleDevice
|
||||||
|
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_HOST
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION)
|
ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION)
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
|
||||||
PHUE_CONFIG_FILE = "phue.conf"
|
PHUE_CONFIG_FILE = "phue.conf"
|
||||||
|
|
||||||
|
|
||||||
def get_lights(hass, config):
|
def get_devices(hass, config):
|
||||||
""" Gets the Hue lights. """
|
""" Gets the Hue lights. """
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
try:
|
try:
|
||||||
|
@ -25,7 +25,7 @@ def get_lights(hass, config):
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
host = config.get(ha.CONF_HOST, None)
|
host = config.get(CONF_HOST, None)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bridge = phue.Bridge(
|
bridge = phue.Bridge(
|
||||||
|
|
|
@ -10,7 +10,7 @@ Author: Markus Stenberg <fingon@iki.fi>
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from homeassistant.components import STATE_ON, STATE_OFF
|
from homeassistant.const import STATE_ON, STATE_OFF
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
|
||||||
DOMAIN = 'process'
|
DOMAIN = 'process'
|
||||||
|
|
|
@ -8,7 +8,9 @@ import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.util as util
|
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
|
||||||
|
from homeassistant.helpers import validate_config
|
||||||
|
from homeassistant.util import str_to_datetime, datetime_to_str
|
||||||
|
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
DOMAIN = "sun"
|
DOMAIN = "sun"
|
||||||
|
@ -35,7 +37,7 @@ def next_setting(hass, entity_id=None):
|
||||||
state = hass.states.get(ENTITY_ID)
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return util.str_to_datetime(state.attributes[STATE_ATTR_NEXT_SETTING])
|
return str_to_datetime(state.attributes[STATE_ATTR_NEXT_SETTING])
|
||||||
except (AttributeError, KeyError):
|
except (AttributeError, KeyError):
|
||||||
# AttributeError if state is None
|
# AttributeError if state is None
|
||||||
# KeyError if STATE_ATTR_NEXT_SETTING does not exist
|
# KeyError if STATE_ATTR_NEXT_SETTING does not exist
|
||||||
|
@ -49,7 +51,7 @@ def next_rising(hass, entity_id=None):
|
||||||
state = hass.states.get(ENTITY_ID)
|
state = hass.states.get(ENTITY_ID)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return util.str_to_datetime(state.attributes[STATE_ATTR_NEXT_RISING])
|
return str_to_datetime(state.attributes[STATE_ATTR_NEXT_RISING])
|
||||||
except (AttributeError, KeyError):
|
except (AttributeError, KeyError):
|
||||||
# AttributeError if state is None
|
# AttributeError if state is None
|
||||||
# KeyError if STATE_ATTR_NEXT_RISING does not exist
|
# KeyError if STATE_ATTR_NEXT_RISING does not exist
|
||||||
|
@ -60,10 +62,9 @@ def setup(hass, config):
|
||||||
""" Tracks the state of the sun. """
|
""" Tracks the state of the sun. """
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
if not util.validate_config(config,
|
if not validate_config(config,
|
||||||
{ha.DOMAIN: [ha.CONF_LATITUDE,
|
{ha.DOMAIN: [CONF_LATITUDE, CONF_LONGITUDE]},
|
||||||
ha.CONF_LONGITUDE]},
|
logger):
|
||||||
logger):
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -74,8 +75,8 @@ def setup(hass, config):
|
||||||
|
|
||||||
sun = ephem.Sun() # pylint: disable=no-member
|
sun = ephem.Sun() # pylint: disable=no-member
|
||||||
|
|
||||||
latitude = config[ha.DOMAIN][ha.CONF_LATITUDE]
|
latitude = config[ha.DOMAIN][CONF_LATITUDE]
|
||||||
longitude = config[ha.DOMAIN][ha.CONF_LONGITUDE]
|
longitude = config[ha.DOMAIN][CONF_LONGITUDE]
|
||||||
|
|
||||||
# Validate latitude and longitude
|
# Validate latitude and longitude
|
||||||
observer = ephem.Observer()
|
observer = ephem.Observer()
|
||||||
|
@ -123,8 +124,8 @@ def setup(hass, config):
|
||||||
new_state, next_change.strftime("%H:%M"))
|
new_state, next_change.strftime("%H:%M"))
|
||||||
|
|
||||||
state_attributes = {
|
state_attributes = {
|
||||||
STATE_ATTR_NEXT_RISING: util.datetime_to_str(next_rising_dt),
|
STATE_ATTR_NEXT_RISING: datetime_to_str(next_rising_dt),
|
||||||
STATE_ATTR_NEXT_SETTING: util.datetime_to_str(next_setting_dt)
|
STATE_ATTR_NEXT_SETTING: datetime_to_str(next_setting_dt)
|
||||||
}
|
}
|
||||||
|
|
||||||
hass.states.set(ENTITY_ID, new_state, state_attributes)
|
hass.states.set(ENTITY_ID, new_state, state_attributes)
|
||||||
|
|
|
@ -6,12 +6,12 @@ Component to interface with various switches that can be controlled remotely.
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import homeassistant as ha
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.loader import get_component
|
from homeassistant.const import (
|
||||||
from homeassistant.components import (
|
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
||||||
group, extract_entity_ids, STATE_ON,
|
from homeassistant.helpers import (
|
||||||
SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
extract_entity_ids, platform_devices_from_config)
|
||||||
|
from homeassistant.components import group
|
||||||
|
|
||||||
DOMAIN = 'switch'
|
DOMAIN = 'switch'
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
@ -53,27 +53,13 @@ def turn_off(hass, entity_id=None):
|
||||||
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
|
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-branches
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Track states and offer events for switches. """
|
""" Track states and offer events for switches. """
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
if not util.validate_config(config, {DOMAIN: [ha.CONF_TYPE]}, logger):
|
switches = platform_devices_from_config(config, DOMAIN, hass, logger)
|
||||||
return False
|
|
||||||
|
|
||||||
switch_type = config[DOMAIN][ha.CONF_TYPE]
|
if not switches:
|
||||||
|
|
||||||
switch_init = get_component('switch.{}'.format(switch_type))
|
|
||||||
|
|
||||||
if switch_init is None:
|
|
||||||
logger.error("Error loading switch component %s", switch_type)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
switches = switch_init.get_switches(hass, config[DOMAIN])
|
|
||||||
|
|
||||||
if len(switches) == 0:
|
|
||||||
logger.error("No switches found")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Setup a dict mapping entity IDs to devices
|
# Setup a dict mapping entity IDs to devices
|
||||||
|
@ -90,7 +76,7 @@ def setup(hass, config):
|
||||||
|
|
||||||
entity_id = util.ensure_unique_string(
|
entity_id = util.ensure_unique_string(
|
||||||
ENTITY_ID_FORMAT.format(util.slugify(name)),
|
ENTITY_ID_FORMAT.format(util.slugify(name)),
|
||||||
list(ent_to_switch.keys()))
|
ent_to_switch.keys())
|
||||||
|
|
||||||
switch.entity_id = entity_id
|
switch.entity_id = entity_id
|
||||||
ent_to_switch[entity_id] = switch
|
ent_to_switch[entity_id] = switch
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
""" Support for Tellstick switches. """
|
""" Support for Tellstick switches. """
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components import ToggleDevice, ATTR_FRIENDLY_NAME
|
from homeassistant.helpers import ToggleDevice
|
||||||
|
from homeassistant.const import ATTR_FRIENDLY_NAME
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import tellcore.constants as tc_constants
|
import tellcore.constants as tc_constants
|
||||||
|
@ -11,7 +12,7 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_switches(hass, config):
|
def get_devices(hass, config):
|
||||||
""" Find and return Tellstick switches. """
|
""" Find and return Tellstick switches. """
|
||||||
try:
|
try:
|
||||||
import tellcore.telldus as telldus
|
import tellcore.telldus as telldus
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
""" Support for WeMo switchces. """
|
""" Support for WeMo switchces. """
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant as ha
|
from homeassistant.helpers import ToggleDevice
|
||||||
from homeassistant.components import ToggleDevice, ATTR_FRIENDLY_NAME
|
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_HOSTS
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def get_switches(hass, config):
|
def get_devices(hass, config):
|
||||||
""" Find and return WeMo switches. """
|
""" Find and return WeMo switches. """
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -21,9 +21,9 @@ def get_switches(hass, config):
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
if ha.CONF_HOSTS in config:
|
if CONF_HOSTS in config:
|
||||||
switches = (pywemo.device_from_host(host) for host
|
switches = (pywemo.device_from_host(host) for host
|
||||||
in config[ha.CONF_HOSTS].split(","))
|
in config[CONF_HOSTS].split(","))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logging.getLogger(__name__).info("Scanning for WeMo devices")
|
logging.getLogger(__name__).info("Scanning for WeMo devices")
|
||||||
|
|
|
@ -26,8 +26,7 @@ import logging
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.components import (ATTR_FRIENDLY_NAME,
|
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
|
||||||
ATTR_UNIT_OF_MEASUREMENT)
|
|
||||||
|
|
||||||
# The domain of your component. Should be equal to the name of your component
|
# The domain of your component. Should be equal to the name of your component
|
||||||
DOMAIN = "tellstick_sensor"
|
DOMAIN = "tellstick_sensor"
|
||||||
|
|
78
homeassistant/const.py
Normal file
78
homeassistant/const.py
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
""" Constants used by Home Assistant components. """
|
||||||
|
# Can be used to specify a catch all when registering state or event listeners.
|
||||||
|
MATCH_ALL = '*'
|
||||||
|
|
||||||
|
# #### CONFIG ####
|
||||||
|
CONF_LATITUDE = "latitude"
|
||||||
|
CONF_LONGITUDE = "longitude"
|
||||||
|
|
||||||
|
# This one is deprecated. Use platform instead.
|
||||||
|
CONF_TYPE = "type"
|
||||||
|
|
||||||
|
CONF_PLATFORM = "platform"
|
||||||
|
CONF_HOST = "host"
|
||||||
|
CONF_HOSTS = "hosts"
|
||||||
|
CONF_USERNAME = "username"
|
||||||
|
CONF_PASSWORD = "password"
|
||||||
|
|
||||||
|
# #### EVENTS ####
|
||||||
|
EVENT_HOMEASSISTANT_START = "homeassistant_start"
|
||||||
|
EVENT_HOMEASSISTANT_STOP = "homeassistant_stop"
|
||||||
|
EVENT_STATE_CHANGED = "state_changed"
|
||||||
|
EVENT_TIME_CHANGED = "time_changed"
|
||||||
|
EVENT_CALL_SERVICE = "services.call"
|
||||||
|
|
||||||
|
# #### STATES ####
|
||||||
|
STATE_ON = 'on'
|
||||||
|
STATE_OFF = 'off'
|
||||||
|
STATE_HOME = 'home'
|
||||||
|
STATE_NOT_HOME = 'not_home'
|
||||||
|
|
||||||
|
# #### STATE ATTRIBUTES ####
|
||||||
|
# Contains current time for a TIME_CHANGED event
|
||||||
|
ATTR_NOW = "now"
|
||||||
|
|
||||||
|
# Contains domain, service for a SERVICE_CALL event
|
||||||
|
ATTR_DOMAIN = "domain"
|
||||||
|
ATTR_SERVICE = "service"
|
||||||
|
|
||||||
|
# Contains one string or a list of strings, each being an entity id
|
||||||
|
ATTR_ENTITY_ID = 'entity_id'
|
||||||
|
|
||||||
|
# String with a friendly name for the entity
|
||||||
|
ATTR_FRIENDLY_NAME = "friendly_name"
|
||||||
|
|
||||||
|
# A picture to represent entity
|
||||||
|
ATTR_ENTITY_PICTURE = "entity_picture"
|
||||||
|
|
||||||
|
# The unit of measurement if applicable
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT = "unit_of_measurement"
|
||||||
|
|
||||||
|
# #### SERVICES ####
|
||||||
|
SERVICE_HOMEASSISTANT_STOP = "stop"
|
||||||
|
|
||||||
|
SERVICE_TURN_ON = 'turn_on'
|
||||||
|
SERVICE_TURN_OFF = 'turn_off'
|
||||||
|
|
||||||
|
SERVICE_VOLUME_UP = "volume_up"
|
||||||
|
SERVICE_VOLUME_DOWN = "volume_down"
|
||||||
|
SERVICE_VOLUME_MUTE = "volume_mute"
|
||||||
|
SERVICE_MEDIA_PLAY_PAUSE = "media_play_pause"
|
||||||
|
SERVICE_MEDIA_PLAY = "media_play"
|
||||||
|
SERVICE_MEDIA_PAUSE = "media_pause"
|
||||||
|
SERVICE_MEDIA_NEXT_TRACK = "media_next_track"
|
||||||
|
SERVICE_MEDIA_PREV_TRACK = "media_prev_track"
|
||||||
|
|
||||||
|
# #### API / REMOTE ####
|
||||||
|
SERVER_PORT = 8123
|
||||||
|
|
||||||
|
AUTH_HEADER = "HA-access"
|
||||||
|
|
||||||
|
URL_API = "/api/"
|
||||||
|
URL_API_STATES = "/api/states"
|
||||||
|
URL_API_STATES_ENTITY = "/api/states/{}"
|
||||||
|
URL_API_EVENTS = "/api/events"
|
||||||
|
URL_API_EVENTS_EVENT = "/api/events/{}"
|
||||||
|
URL_API_SERVICES = "/api/services"
|
||||||
|
URL_API_SERVICES_SERVICE = "/api/services/{}/{}"
|
||||||
|
URL_API_EVENT_FORWARD = "/api/event_forwarding"
|
176
homeassistant/helpers.py
Normal file
176
homeassistant/helpers.py
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
"""
|
||||||
|
Helper methods for components within Home Assistant.
|
||||||
|
"""
|
||||||
|
from homeassistant import NoEntitySpecifiedError
|
||||||
|
|
||||||
|
from homeassistant.loader import get_component
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID, STATE_ON, STATE_OFF, CONF_PLATFORM, CONF_TYPE)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_entity_ids(hass, service):
|
||||||
|
"""
|
||||||
|
Helper method to extract a list of entity ids from a service call.
|
||||||
|
Will convert group entity ids to the entity ids it represents.
|
||||||
|
"""
|
||||||
|
entity_ids = []
|
||||||
|
|
||||||
|
if service.data and ATTR_ENTITY_ID in service.data:
|
||||||
|
group = get_component('group')
|
||||||
|
|
||||||
|
# Entity ID attr can be a list or a string
|
||||||
|
service_ent_id = service.data[ATTR_ENTITY_ID]
|
||||||
|
if isinstance(service_ent_id, list):
|
||||||
|
ent_ids = service_ent_id
|
||||||
|
else:
|
||||||
|
ent_ids = [service_ent_id]
|
||||||
|
|
||||||
|
entity_ids.extend(
|
||||||
|
ent_id for ent_id
|
||||||
|
in group.expand_entity_ids(hass, ent_ids)
|
||||||
|
if ent_id not in entity_ids)
|
||||||
|
|
||||||
|
return entity_ids
|
||||||
|
|
||||||
|
|
||||||
|
def validate_config(config, items, logger):
|
||||||
|
"""
|
||||||
|
Validates if all items are available in the configuration.
|
||||||
|
|
||||||
|
config is the general dictionary with all the configurations.
|
||||||
|
items is a dict with per domain which attributes we require.
|
||||||
|
logger is the logger from the caller to log the errors to.
|
||||||
|
|
||||||
|
Returns True if all required items were found.
|
||||||
|
"""
|
||||||
|
errors_found = False
|
||||||
|
for domain in items.keys():
|
||||||
|
config.setdefault(domain, {})
|
||||||
|
|
||||||
|
errors = [item for item in items[domain] if item not in config[domain]]
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
logger.error(
|
||||||
|
"Missing required configuration items in {}: {}".format(
|
||||||
|
domain, ", ".join(errors)))
|
||||||
|
|
||||||
|
errors_found = True
|
||||||
|
|
||||||
|
return not errors_found
|
||||||
|
|
||||||
|
|
||||||
|
def config_per_platform(config, domain, logger):
|
||||||
|
"""
|
||||||
|
Generator to break a component config into different platforms.
|
||||||
|
For example, will find 'switch', 'switch 2', 'switch 3', .. etc
|
||||||
|
"""
|
||||||
|
config_key = domain
|
||||||
|
found = 1
|
||||||
|
|
||||||
|
while config_key in config:
|
||||||
|
platform_config = config[config_key]
|
||||||
|
|
||||||
|
platform_type = platform_config.get(CONF_PLATFORM)
|
||||||
|
|
||||||
|
# DEPRECATED, still supported for now.
|
||||||
|
if platform_type is None:
|
||||||
|
platform_type = platform_config.get(CONF_TYPE)
|
||||||
|
|
||||||
|
if platform_type is not None:
|
||||||
|
logger.warning((
|
||||||
|
'Please update your config for {}.{} to use "platform" '
|
||||||
|
'instead of "type"').format(domain, platform_type))
|
||||||
|
|
||||||
|
if platform_type is None:
|
||||||
|
logger.warning('No platform specified for %s', config_key)
|
||||||
|
break
|
||||||
|
|
||||||
|
yield platform_type, platform_config
|
||||||
|
|
||||||
|
found += 1
|
||||||
|
config_key = "{} {}".format(domain, found)
|
||||||
|
|
||||||
|
|
||||||
|
def platform_devices_from_config(config, domain, hass, logger):
|
||||||
|
""" Parses the config for specified domain.
|
||||||
|
Loads different platforms and retrieve domains. """
|
||||||
|
devices = []
|
||||||
|
|
||||||
|
for p_type, p_config in config_per_platform(config, domain, logger):
|
||||||
|
platform = get_component('{}.{}'.format(domain, p_type))
|
||||||
|
|
||||||
|
if platform is None:
|
||||||
|
logger.error("Unknown %s type specified: %s", domain, p_type)
|
||||||
|
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
p_devices = platform.get_devices(hass, p_config)
|
||||||
|
except AttributeError:
|
||||||
|
# DEPRECATED, still supported for now
|
||||||
|
logger.warning(
|
||||||
|
'Platform %s should migrate to use the method get_devices',
|
||||||
|
p_type)
|
||||||
|
|
||||||
|
if domain == 'light':
|
||||||
|
p_devices = platform.get_lights(hass, p_config)
|
||||||
|
elif domain == 'switch':
|
||||||
|
p_devices = platform.get_switches(hass, p_config)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
logger.info("Found %d %s %ss", len(p_devices), p_type, domain)
|
||||||
|
|
||||||
|
devices.extend(p_devices)
|
||||||
|
|
||||||
|
if len(devices) == 0:
|
||||||
|
logger.error("No devices found for %s", domain)
|
||||||
|
|
||||||
|
return devices
|
||||||
|
|
||||||
|
|
||||||
|
class ToggleDevice(object):
|
||||||
|
""" ABC for devices that can be turned on and off. """
|
||||||
|
# pylint: disable=no-self-use
|
||||||
|
|
||||||
|
entity_id = None
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
""" Returns the name of the device if any. """
|
||||||
|
return None
|
||||||
|
|
||||||
|
def turn_on(self, **kwargs):
|
||||||
|
""" Turn the device on. """
|
||||||
|
pass
|
||||||
|
|
||||||
|
def turn_off(self, **kwargs):
|
||||||
|
""" Turn the device off. """
|
||||||
|
pass
|
||||||
|
|
||||||
|
def is_on(self):
|
||||||
|
""" True if device is on. """
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_state_attributes(self):
|
||||||
|
""" Returns optional state attributes. """
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
""" Retrieve latest state from the real device. """
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update_ha_state(self, hass, force_refresh=False):
|
||||||
|
"""
|
||||||
|
Updates Home Assistant with current state of device.
|
||||||
|
If force_refresh == True will update device before setting state.
|
||||||
|
"""
|
||||||
|
if self.entity_id is None:
|
||||||
|
raise NoEntitySpecifiedError(
|
||||||
|
"No entity specified for device {}".format(self.get_name()))
|
||||||
|
|
||||||
|
if force_refresh:
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
state = STATE_ON if self.is_on() else STATE_OFF
|
||||||
|
|
||||||
|
return hass.states.set(self.entity_id, state,
|
||||||
|
self.get_state_attributes())
|
|
@ -19,18 +19,10 @@ import requests
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
|
|
||||||
SERVER_PORT = 8123
|
from homeassistant.const import (
|
||||||
|
SERVER_PORT, AUTH_HEADER, URL_API, URL_API_STATES, URL_API_STATES_ENTITY,
|
||||||
AUTH_HEADER = "HA-access"
|
URL_API_EVENTS, URL_API_EVENTS_EVENT, URL_API_SERVICES,
|
||||||
|
URL_API_SERVICES_SERVICE, URL_API_EVENT_FORWARD)
|
||||||
URL_API = "/api/"
|
|
||||||
URL_API_STATES = "/api/states"
|
|
||||||
URL_API_STATES_ENTITY = "/api/states/{}"
|
|
||||||
URL_API_EVENTS = "/api/events"
|
|
||||||
URL_API_EVENTS_EVENT = "/api/events/{}"
|
|
||||||
URL_API_SERVICES = "/api/services"
|
|
||||||
URL_API_SERVICES_SERVICE = "/api/services/{}/{}"
|
|
||||||
URL_API_EVENT_FORWARD = "/api/event_forwarding"
|
|
||||||
|
|
||||||
METHOD_GET = "get"
|
METHOD_GET = "get"
|
||||||
METHOD_POST = "post"
|
METHOD_POST = "post"
|
||||||
|
|
|
@ -127,6 +127,7 @@ def ensure_unique_string(preferred_string, current_strings):
|
||||||
""" Returns a string that is not present in current_strings.
|
""" Returns a string that is not present in current_strings.
|
||||||
If preferred string exists will append _2, _3, .. """
|
If preferred string exists will append _2, _3, .. """
|
||||||
string = preferred_string
|
string = preferred_string
|
||||||
|
current_strings = list(current_strings)
|
||||||
|
|
||||||
tries = 1
|
tries = 1
|
||||||
|
|
||||||
|
@ -248,32 +249,6 @@ class OrderedSet(collections.MutableSet):
|
||||||
return set(self) == set(other)
|
return set(self) == set(other)
|
||||||
|
|
||||||
|
|
||||||
def validate_config(config, items, logger):
|
|
||||||
"""
|
|
||||||
Validates if all items are available in the configuration.
|
|
||||||
|
|
||||||
config is the general dictionary with all the configurations.
|
|
||||||
items is a dict with per domain which attributes we require.
|
|
||||||
logger is the logger from the caller to log the errors to.
|
|
||||||
|
|
||||||
Returns True if all required items were found.
|
|
||||||
"""
|
|
||||||
errors_found = False
|
|
||||||
for domain in items.keys():
|
|
||||||
config.setdefault(domain, {})
|
|
||||||
|
|
||||||
errors = [item for item in items[domain] if item not in config[domain]]
|
|
||||||
|
|
||||||
if errors:
|
|
||||||
logger.error(
|
|
||||||
"Missing required configuration items in {}: {}".format(
|
|
||||||
domain, ", ".join(errors)))
|
|
||||||
|
|
||||||
errors_found = True
|
|
||||||
|
|
||||||
return not errors_found
|
|
||||||
|
|
||||||
|
|
||||||
class Throttle(object):
|
class Throttle(object):
|
||||||
"""
|
"""
|
||||||
A method decorator to add a cooldown to a method to prevent it from being
|
A method decorator to add a cooldown to a method to prevent it from being
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue