mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 09:17:10 +00:00
Expose all components on hass [Concept] (#8490)
* Add components concept * Lint * Raise ImportError if component not found
This commit is contained in:
parent
bffa0d2b04
commit
d3be056d15
@ -13,6 +13,7 @@ import csv
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.components import group
|
from homeassistant.components import group
|
||||||
from homeassistant.config import load_yaml_config_file
|
from homeassistant.config import load_yaml_config_file
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@ -165,6 +166,7 @@ def turn_on(hass, entity_id=None, transition=None, brightness=None,
|
|||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@bind_hass
|
||||||
def async_turn_on(hass, entity_id=None, transition=None, brightness=None,
|
def async_turn_on(hass, entity_id=None, transition=None, brightness=None,
|
||||||
brightness_pct=None, rgb_color=None, xy_color=None,
|
brightness_pct=None, rgb_color=None, xy_color=None,
|
||||||
color_temp=None, kelvin=None, white_value=None,
|
color_temp=None, kelvin=None, white_value=None,
|
||||||
|
@ -30,6 +30,7 @@ from homeassistant.const import (
|
|||||||
EVENT_SERVICE_EXECUTED, EVENT_SERVICE_REGISTERED, EVENT_STATE_CHANGED,
|
EVENT_SERVICE_EXECUTED, EVENT_SERVICE_REGISTERED, EVENT_STATE_CHANGED,
|
||||||
EVENT_TIME_CHANGED, MATCH_ALL, EVENT_HOMEASSISTANT_CLOSE,
|
EVENT_TIME_CHANGED, MATCH_ALL, EVENT_HOMEASSISTANT_CLOSE,
|
||||||
EVENT_SERVICE_REMOVED, __version__)
|
EVENT_SERVICE_REMOVED, __version__)
|
||||||
|
from homeassistant.loader import Components
|
||||||
from homeassistant.exceptions import (
|
from homeassistant.exceptions import (
|
||||||
HomeAssistantError, InvalidEntityFormatError)
|
HomeAssistantError, InvalidEntityFormatError)
|
||||||
from homeassistant.util.async import (
|
from homeassistant.util.async import (
|
||||||
@ -128,6 +129,7 @@ class HomeAssistant(object):
|
|||||||
self.services = ServiceRegistry(self)
|
self.services = ServiceRegistry(self)
|
||||||
self.states = StateMachine(self.bus, self.loop)
|
self.states = StateMachine(self.bus, self.loop)
|
||||||
self.config = Config() # type: Config
|
self.config = Config() # type: Config
|
||||||
|
self.components = Components(self)
|
||||||
# This is a dictionary that any component can store any data on.
|
# This is a dictionary that any component can store any data on.
|
||||||
self.data = {}
|
self.data = {}
|
||||||
self.state = CoreState.not_running
|
self.state = CoreState.not_running
|
||||||
|
@ -10,6 +10,7 @@ call get_component('switch.your_platform'). In both cases the config directory
|
|||||||
is checked to see if it contains a user provided version. If not available it
|
is checked to see if it contains a user provided version. If not available it
|
||||||
will check the built-in components and platforms.
|
will check the built-in components and platforms.
|
||||||
"""
|
"""
|
||||||
|
import functools as ft
|
||||||
import importlib
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -170,6 +171,49 @@ def get_component(comp_name) -> Optional[ModuleType]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class Components:
|
||||||
|
"""Helper to load components."""
|
||||||
|
|
||||||
|
def __init__(self, hass):
|
||||||
|
"""Initialize the Components class."""
|
||||||
|
self._hass = hass
|
||||||
|
|
||||||
|
def __getattr__(self, comp_name):
|
||||||
|
"""Fetch a component."""
|
||||||
|
component = get_component(comp_name)
|
||||||
|
if component is None:
|
||||||
|
raise ImportError('Unable to load {}'.format(comp_name))
|
||||||
|
wrapped = ComponentWrapper(self._hass, component)
|
||||||
|
setattr(self, comp_name, wrapped)
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentWrapper:
|
||||||
|
"""Class to wrap a component and auto fill in hass argument."""
|
||||||
|
|
||||||
|
def __init__(self, hass, component):
|
||||||
|
"""Initialize the component wrapper."""
|
||||||
|
self._hass = hass
|
||||||
|
self._component = component
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
"""Fetch an attribute."""
|
||||||
|
value = getattr(self._component, attr)
|
||||||
|
|
||||||
|
if hasattr(value, '__bind_hass'):
|
||||||
|
value = ft.partial(value, self._hass)
|
||||||
|
|
||||||
|
setattr(self, attr, value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def bind_hass(func):
|
||||||
|
"""Decorator to indicate that first argument is hass."""
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
func.__bind_hass = True
|
||||||
|
return func
|
||||||
|
|
||||||
|
|
||||||
def load_order_component(comp_name: str) -> OrderedSet:
|
def load_order_component(comp_name: str) -> OrderedSet:
|
||||||
"""Return an OrderedSet of components in the correct order of loading.
|
"""Return an OrderedSet of components in the correct order of loading.
|
||||||
|
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
"""Test to verify that we can load components."""
|
"""Test to verify that we can load components."""
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
|
import asyncio
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
import homeassistant.components.http as http
|
import homeassistant.components.http as http
|
||||||
|
|
||||||
from tests.common import get_test_home_assistant, MockModule
|
from tests.common import (
|
||||||
|
get_test_home_assistant, MockModule, async_mock_service)
|
||||||
|
|
||||||
|
|
||||||
class TestLoader(unittest.TestCase):
|
class TestLoader(unittest.TestCase):
|
||||||
@ -54,3 +58,29 @@ class TestLoader(unittest.TestCase):
|
|||||||
|
|
||||||
# Try to get load order for non-existing component
|
# Try to get load order for non-existing component
|
||||||
self.assertEqual([], loader.load_order_component('mod1'))
|
self.assertEqual([], loader.load_order_component('mod1'))
|
||||||
|
|
||||||
|
|
||||||
|
def test_component_loader(hass):
|
||||||
|
"""Test loading components."""
|
||||||
|
components = loader.Components(hass)
|
||||||
|
assert components.http.CONFIG_SCHEMA is http.CONFIG_SCHEMA
|
||||||
|
assert hass.components.http.CONFIG_SCHEMA is http.CONFIG_SCHEMA
|
||||||
|
|
||||||
|
|
||||||
|
def test_component_loader_non_existing(hass):
|
||||||
|
"""Test loading components."""
|
||||||
|
components = loader.Components(hass)
|
||||||
|
with pytest.raises(ImportError):
|
||||||
|
components.non_existing
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_component_wrapper(hass):
|
||||||
|
"""Test component wrapper."""
|
||||||
|
calls = async_mock_service(hass, 'light', 'turn_on')
|
||||||
|
|
||||||
|
components = loader.Components(hass)
|
||||||
|
components.light.async_turn_on('light.test')
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user