mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Refactor: loading of components now done in a seperate module + better error reporting
This commit is contained in:
parent
3c37f491b2
commit
a9ee2f9c54
@ -61,24 +61,11 @@ class HomeAssistant(object):
|
|||||||
self.services = ServiceRegistry(self.bus, pool)
|
self.services = ServiceRegistry(self.bus, pool)
|
||||||
self.states = StateMachine(self.bus)
|
self.states = StateMachine(self.bus)
|
||||||
|
|
||||||
self._config_dir = os.getcwd()
|
self.config_dir = os.getcwd()
|
||||||
|
|
||||||
@property
|
def get_config_path(self, path):
|
||||||
def config_dir(self):
|
|
||||||
""" Return value of config dir. """
|
|
||||||
return self._config_dir
|
|
||||||
|
|
||||||
@config_dir.setter
|
|
||||||
def config_dir(self, value):
|
|
||||||
""" Update value of config dir and ensures it's in Python path. """
|
|
||||||
self._config_dir = value
|
|
||||||
|
|
||||||
# Ensure we can load components from the config dir
|
|
||||||
sys.path.append(value)
|
|
||||||
|
|
||||||
def get_config_path(self, sub_path):
|
|
||||||
""" Returns path to the file within the config dir. """
|
""" Returns path to the file within the config dir. """
|
||||||
return os.path.join(self._config_dir, sub_path)
|
return os.path.join(self.config_dir, path)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
""" Start home assistant. """
|
""" Start home assistant. """
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
|
homeassistant.bootstrap
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Provides methods to bootstrap a home assistant instance.
|
Provides methods to bootstrap a home assistant instance.
|
||||||
|
|
||||||
Each method will return a tuple (bus, statemachine).
|
Each method will return a tuple (bus, statemachine).
|
||||||
@ -14,6 +16,7 @@ from collections import defaultdict
|
|||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
import homeassistant
|
import homeassistant
|
||||||
|
import homeassistant.loader as loader
|
||||||
import homeassistant.components as core_components
|
import homeassistant.components as core_components
|
||||||
import homeassistant.components.group as group
|
import homeassistant.components.group as group
|
||||||
|
|
||||||
@ -46,11 +49,13 @@ def from_config_dict(config, hass=None):
|
|||||||
# List of components we are going to load
|
# List of components we are going to load
|
||||||
to_load = [key for key in config.keys() if key != homeassistant.DOMAIN]
|
to_load = [key for key in config.keys() if key != homeassistant.DOMAIN]
|
||||||
|
|
||||||
|
loader.prepare(hass)
|
||||||
|
|
||||||
# Load required components
|
# Load required components
|
||||||
while to_load:
|
while to_load:
|
||||||
domain = to_load.pop()
|
domain = to_load.pop()
|
||||||
|
|
||||||
component = core_components.get_component(domain, logger)
|
component = loader.get_component(domain)
|
||||||
|
|
||||||
# if None it does not exist, error already thrown by get_component
|
# if None it does not exist, error already thrown by get_component
|
||||||
if component is not None:
|
if component is not None:
|
||||||
@ -123,7 +128,7 @@ def from_config_dict(config, hass=None):
|
|||||||
|
|
||||||
if group.DOMAIN not in components:
|
if group.DOMAIN not in components:
|
||||||
components[group.DOMAIN] = \
|
components[group.DOMAIN] = \
|
||||||
core_components.get_component(group.DOMAIN, logger)
|
loader.get_component(group.DOMAIN)
|
||||||
|
|
||||||
# Setup the components
|
# Setup the components
|
||||||
if core_components.setup(hass, config):
|
if core_components.setup(hass, config):
|
||||||
|
@ -20,6 +20,7 @@ import importlib
|
|||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
from homeassistant.loader import get_component
|
||||||
|
|
||||||
# Contains one string or a list of strings, each being an entity id
|
# Contains one string or a list of strings, each being an entity id
|
||||||
ATTR_ENTITY_ID = 'entity_id'
|
ATTR_ENTITY_ID = 'entity_id'
|
||||||
@ -47,80 +48,14 @@ SERVICE_MEDIA_PAUSE = "media_pause"
|
|||||||
SERVICE_MEDIA_NEXT_TRACK = "media_next_track"
|
SERVICE_MEDIA_NEXT_TRACK = "media_next_track"
|
||||||
SERVICE_MEDIA_PREV_TRACK = "media_prev_track"
|
SERVICE_MEDIA_PREV_TRACK = "media_prev_track"
|
||||||
|
|
||||||
_COMPONENT_CACHE = {}
|
__LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_component(comp_name, logger=None):
|
|
||||||
""" Tries to load specified component.
|
|
||||||
Looks in config dir first, then built-in components.
|
|
||||||
Only returns it if also found to be valid. """
|
|
||||||
|
|
||||||
if comp_name in _COMPONENT_CACHE:
|
|
||||||
return _COMPONENT_CACHE[comp_name]
|
|
||||||
|
|
||||||
# First config dir, then built-in
|
|
||||||
potential_paths = ['custom_components.{}'.format(comp_name),
|
|
||||||
'homeassistant.components.{}'.format(comp_name)]
|
|
||||||
|
|
||||||
for path in potential_paths:
|
|
||||||
comp = _get_component(path, logger)
|
|
||||||
|
|
||||||
if comp is not None:
|
|
||||||
if logger is not None:
|
|
||||||
logger.info("Loaded component {} from {}".format(
|
|
||||||
comp_name, path))
|
|
||||||
|
|
||||||
_COMPONENT_CACHE[comp_name] = comp
|
|
||||||
|
|
||||||
return comp
|
|
||||||
|
|
||||||
# We did not find a component
|
|
||||||
if logger is not None:
|
|
||||||
logger.error(
|
|
||||||
"Failed to find component {}".format(comp_name))
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _get_component(module, logger):
|
|
||||||
""" Tries to load specified component.
|
|
||||||
Only returns it if also found to be valid."""
|
|
||||||
try:
|
|
||||||
comp = importlib.import_module(module)
|
|
||||||
|
|
||||||
except ImportError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Validation if component has required methods and attributes
|
|
||||||
errors = []
|
|
||||||
|
|
||||||
if not hasattr(comp, 'DOMAIN'):
|
|
||||||
errors.append("Missing DOMAIN attribute")
|
|
||||||
|
|
||||||
if not hasattr(comp, 'DEPENDENCIES'):
|
|
||||||
errors.append("Missing DEPENDENCIES attribute")
|
|
||||||
|
|
||||||
if not hasattr(comp, 'setup'):
|
|
||||||
errors.append("Missing setup method")
|
|
||||||
|
|
||||||
if errors:
|
|
||||||
if logger:
|
|
||||||
logger.error("Found invalid component {}: {}".format(
|
|
||||||
module, ", ".join(errors)))
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
else:
|
|
||||||
return comp
|
|
||||||
|
|
||||||
|
|
||||||
def is_on(hass, entity_id=None):
|
def is_on(hass, entity_id=None):
|
||||||
""" Loads up the module to call the is_on method.
|
""" Loads up the module to call the is_on method.
|
||||||
If there is no entity id given we will check all. """
|
If there is no entity id given we will check all. """
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
if entity_id:
|
if entity_id:
|
||||||
group = get_component('group', logger)
|
group = get_component('group')
|
||||||
|
|
||||||
entity_ids = group.expand_entity_ids([entity_id])
|
entity_ids = group.expand_entity_ids([entity_id])
|
||||||
else:
|
else:
|
||||||
@ -129,7 +64,7 @@ def is_on(hass, entity_id=None):
|
|||||||
for entity_id in entity_ids:
|
for entity_id in entity_ids:
|
||||||
domain = util.split_entity_id(entity_id)[0]
|
domain = util.split_entity_id(entity_id)[0]
|
||||||
|
|
||||||
module = get_component(domain, logger)
|
module = get_component(domain)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if module.is_on(hass, entity_id):
|
if module.is_on(hass, entity_id):
|
||||||
|
@ -7,10 +7,10 @@ Sets up a demo environment that mimics interaction with devices
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.components.group as group
|
import homeassistant.loader as loader
|
||||||
from homeassistant.components import (SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
from homeassistant.components import (SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
||||||
STATE_ON, STATE_OFF, ATTR_ENTITY_PICTURE,
|
STATE_ON, STATE_OFF, ATTR_ENTITY_PICTURE,
|
||||||
get_component, extract_entity_ids)
|
extract_entity_ids)
|
||||||
from homeassistant.components.light import (ATTR_XY_COLOR, ATTR_BRIGHTNESS,
|
from homeassistant.components.light import (ATTR_XY_COLOR, ATTR_BRIGHTNESS,
|
||||||
GROUP_NAME_ALL_LIGHTS)
|
GROUP_NAME_ALL_LIGHTS)
|
||||||
from homeassistant.util import split_entity_id
|
from homeassistant.util import split_entity_id
|
||||||
@ -22,6 +22,7 @@ DEPENDENCIES = []
|
|||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup a demo environment. """
|
""" Setup a demo environment. """
|
||||||
|
group = loader.get_component('group')
|
||||||
|
|
||||||
if config[DOMAIN].get('hide_demo_state') != '1':
|
if config[DOMAIN].get('hide_demo_state') != '1':
|
||||||
hass.states.set('a.Demo_Mode', 'Enabled')
|
hass.states.set('a.Demo_Mode', 'Enabled')
|
||||||
@ -57,7 +58,7 @@ def setup(hass, config):
|
|||||||
if ha.CONF_LONGITUDE not in config[ha.DOMAIN]:
|
if ha.CONF_LONGITUDE not in config[ha.DOMAIN]:
|
||||||
config[ha.DOMAIN][ha.CONF_LONGITUDE] = '-117.22743'
|
config[ha.DOMAIN][ha.CONF_LONGITUDE] = '-117.22743'
|
||||||
|
|
||||||
get_component('sun').setup(hass, config)
|
loader.get_component('sun').setup(hass, config)
|
||||||
|
|
||||||
# Setup fake lights
|
# Setup fake lights
|
||||||
lights = ['light.Bowl', 'light.Ceiling', 'light.TV_Back_light',
|
lights = ['light.Bowl', 'light.Ceiling', 'light.TV_Back_light',
|
||||||
|
107
homeassistant/loader.py
Normal file
107
homeassistant/loader.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.loader
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Provides methods for loading Home Assistant components.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import pkgutil
|
||||||
|
import importlib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# List of available components
|
||||||
|
AVAILABLE_COMPONENTS = []
|
||||||
|
|
||||||
|
# Dict of loaded components mapped name => module
|
||||||
|
_COMPONENT_CACHE = {}
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare(hass):
|
||||||
|
""" Prepares the loading of components. """
|
||||||
|
# Ensure we can load custom components from the config dir
|
||||||
|
sys.path.append(hass.config_dir)
|
||||||
|
|
||||||
|
# pylint: disable=import-error
|
||||||
|
import custom_components
|
||||||
|
import homeassistant.components as components
|
||||||
|
|
||||||
|
AVAILABLE_COMPONENTS.clear()
|
||||||
|
|
||||||
|
AVAILABLE_COMPONENTS.extend(
|
||||||
|
item[1] for item in
|
||||||
|
pkgutil.iter_modules(components.__path__, 'homeassistant.components.'))
|
||||||
|
|
||||||
|
AVAILABLE_COMPONENTS.extend(
|
||||||
|
item[1] for item in
|
||||||
|
pkgutil.iter_modules(custom_components.__path__, 'custom_components.'))
|
||||||
|
|
||||||
|
|
||||||
|
def get_component(comp_name):
|
||||||
|
""" Tries to load specified component.
|
||||||
|
Looks in config dir first, then built-in components.
|
||||||
|
Only returns it if also found to be valid. """
|
||||||
|
|
||||||
|
if comp_name in _COMPONENT_CACHE:
|
||||||
|
return _COMPONENT_CACHE[comp_name]
|
||||||
|
|
||||||
|
# First check config dir, then built-in
|
||||||
|
potential_paths = [path for path in
|
||||||
|
['custom_components.{}'.format(comp_name),
|
||||||
|
'homeassistant.components.{}'.format(comp_name)]
|
||||||
|
if path in AVAILABLE_COMPONENTS]
|
||||||
|
|
||||||
|
if not potential_paths:
|
||||||
|
_LOGGER.error("Failed to find component {}".format(comp_name))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
for path in potential_paths:
|
||||||
|
comp = _get_component(path)
|
||||||
|
|
||||||
|
if comp is not None:
|
||||||
|
_LOGGER.info("Loaded component {} from {}".format(
|
||||||
|
comp_name, path))
|
||||||
|
|
||||||
|
_COMPONENT_CACHE[comp_name] = comp
|
||||||
|
|
||||||
|
return comp
|
||||||
|
|
||||||
|
# We did find components but were unable to load them
|
||||||
|
_LOGGER.error("Unable to load component {}".format(comp_name))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_component(module):
|
||||||
|
""" Tries to load specified component.
|
||||||
|
Only returns it if also found to be valid."""
|
||||||
|
try:
|
||||||
|
comp = importlib.import_module(module)
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
_LOGGER.exception("Error loading {}".format(module))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Validation if component has required methods and attributes
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
if not hasattr(comp, 'DOMAIN'):
|
||||||
|
errors.append("missing DOMAIN attribute")
|
||||||
|
|
||||||
|
if not hasattr(comp, 'DEPENDENCIES'):
|
||||||
|
errors.append("missing DEPENDENCIES attribute")
|
||||||
|
|
||||||
|
if not hasattr(comp, 'setup'):
|
||||||
|
errors.append("missing setup method")
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
_LOGGER.error("Found invalid component {}: {}".format(
|
||||||
|
module, ", ".join(errors)))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
else:
|
||||||
|
return comp
|
Loading…
x
Reference in New Issue
Block a user