Refactor: code moved to new helper and constants file. Also adds support for multiple types for switch/light components.

This commit is contained in:
Paulus Schoutsen 2014-12-06 23:57:02 -08:00
parent 513a03fb46
commit 0527760e9b
41 changed files with 603 additions and 442 deletions

View file

@ -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

View file

@ -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

View file

@ -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)
] ]

View file

@ -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)
] ]

View file

@ -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:

View file

@ -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'}}))

View file

@ -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))

View file

@ -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)

View file

@ -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.

View file

@ -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])

View file

@ -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)

View file

@ -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'
}})) }}))

View file

@ -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
View 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))

View file

@ -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):

View file

@ -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

View file

@ -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):

View file

@ -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. """

View file

@ -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",

View file

@ -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)

View file

@ -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

View file

@ -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))

View file

@ -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. """

View file

@ -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. """

View file

@ -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. """

View file

@ -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:

View file

@ -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 = []

View file

@ -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. """

View file

@ -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))

View file

@ -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

View file

@ -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(

View file

@ -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'

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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
View 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
View 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())

View file

@ -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"

View file

@ -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