mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Fix PEP257 issues
This commit is contained in:
parent
4f536ac63d
commit
897b5c668f
@ -0,0 +1 @@
|
|||||||
|
"""Init file for Home Assistant."""
|
@ -31,8 +31,7 @@ def validate_python():
|
|||||||
|
|
||||||
|
|
||||||
def ensure_config_path(config_dir):
|
def ensure_config_path(config_dir):
|
||||||
""" Validates configuration directory. """
|
"""Validate the configuration directory."""
|
||||||
|
|
||||||
lib_dir = os.path.join(config_dir, 'lib')
|
lib_dir = os.path.join(config_dir, 'lib')
|
||||||
|
|
||||||
# Test if configuration directory exists
|
# Test if configuration directory exists
|
||||||
@ -136,25 +135,25 @@ def get_arguments():
|
|||||||
|
|
||||||
|
|
||||||
def daemonize():
|
def daemonize():
|
||||||
""" Move current process to daemon process """
|
"""Move current process to daemon process."""
|
||||||
# create first fork
|
# Create first fork
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
if pid > 0:
|
if pid > 0:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# decouple fork
|
# Decouple fork
|
||||||
os.setsid()
|
os.setsid()
|
||||||
os.umask(0)
|
os.umask(0)
|
||||||
|
|
||||||
# create second fork
|
# Create second fork
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
if pid > 0:
|
if pid > 0:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def check_pid(pid_file):
|
def check_pid(pid_file):
|
||||||
""" Check that HA is not already running """
|
"""Check that HA is not already running."""
|
||||||
# check pid file
|
# Check pid file
|
||||||
try:
|
try:
|
||||||
pid = int(open(pid_file, 'r').readline())
|
pid = int(open(pid_file, 'r').readline())
|
||||||
except IOError:
|
except IOError:
|
||||||
@ -171,7 +170,7 @@ def check_pid(pid_file):
|
|||||||
|
|
||||||
|
|
||||||
def write_pid(pid_file):
|
def write_pid(pid_file):
|
||||||
""" Create PID File """
|
"""Create a PID File."""
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
try:
|
try:
|
||||||
open(pid_file, 'w').write(str(pid))
|
open(pid_file, 'w').write(str(pid))
|
||||||
@ -181,7 +180,7 @@ def write_pid(pid_file):
|
|||||||
|
|
||||||
|
|
||||||
def install_osx():
|
def install_osx():
|
||||||
""" Setup to run via launchd on OS X """
|
"""Setup to run via launchd on OS X."""
|
||||||
with os.popen('which hass') as inp:
|
with os.popen('which hass') as inp:
|
||||||
hass_path = inp.read().strip()
|
hass_path = inp.read().strip()
|
||||||
|
|
||||||
@ -213,7 +212,7 @@ def install_osx():
|
|||||||
|
|
||||||
|
|
||||||
def uninstall_osx():
|
def uninstall_osx():
|
||||||
""" Unload from launchd on OS X """
|
"""Unload from launchd on OS X."""
|
||||||
path = os.path.expanduser("~/Library/LaunchAgents/org.homeassistant.plist")
|
path = os.path.expanduser("~/Library/LaunchAgents/org.homeassistant.plist")
|
||||||
os.popen('launchctl unload ' + path)
|
os.popen('launchctl unload ' + path)
|
||||||
|
|
||||||
@ -221,9 +220,10 @@ def uninstall_osx():
|
|||||||
|
|
||||||
|
|
||||||
def setup_and_run_hass(config_dir, args, top_process=False):
|
def setup_and_run_hass(config_dir, args, top_process=False):
|
||||||
"""
|
"""Setup HASS and run.
|
||||||
Setup HASS and run. Block until stopped. Will assume it is running in a
|
|
||||||
subprocess unless top_process is set to true.
|
Block until stopped. Will assume it is running in a subprocess unless
|
||||||
|
top_process is set to true.
|
||||||
"""
|
"""
|
||||||
if args.demo_mode:
|
if args.demo_mode:
|
||||||
config = {
|
config = {
|
||||||
@ -259,12 +259,12 @@ def setup_and_run_hass(config_dir, args, top_process=False):
|
|||||||
|
|
||||||
|
|
||||||
def run_hass_process(hass_proc):
|
def run_hass_process(hass_proc):
|
||||||
""" Runs a child hass process. Returns True if it should be restarted. """
|
"""Run a child hass process. Returns True if it should be restarted."""
|
||||||
requested_stop = threading.Event()
|
requested_stop = threading.Event()
|
||||||
hass_proc.daemon = True
|
hass_proc.daemon = True
|
||||||
|
|
||||||
def request_stop(*args):
|
def request_stop(*args):
|
||||||
""" request hass stop, *args is for signal handler callback """
|
"""Request hass stop, *args is for signal handler callback."""
|
||||||
requested_stop.set()
|
requested_stop.set()
|
||||||
hass_proc.terminate()
|
hass_proc.terminate()
|
||||||
|
|
||||||
@ -289,7 +289,7 @@ def run_hass_process(hass_proc):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" Starts Home Assistant. """
|
"""Start Home Assistant."""
|
||||||
validate_python()
|
validate_python()
|
||||||
|
|
||||||
args = get_arguments()
|
args = get_arguments()
|
||||||
@ -297,7 +297,7 @@ def main():
|
|||||||
config_dir = os.path.join(os.getcwd(), args.config)
|
config_dir = os.path.join(os.getcwd(), args.config)
|
||||||
ensure_config_path(config_dir)
|
ensure_config_path(config_dir)
|
||||||
|
|
||||||
# os x launchd functions
|
# OS X launchd functions
|
||||||
if args.install_osx:
|
if args.install_osx:
|
||||||
install_osx()
|
install_osx()
|
||||||
return 0
|
return 0
|
||||||
@ -311,7 +311,7 @@ def main():
|
|||||||
install_osx()
|
install_osx()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# daemon functions
|
# Daemon functions
|
||||||
if args.pid_file:
|
if args.pid_file:
|
||||||
check_pid(args.pid_file)
|
check_pid(args.pid_file)
|
||||||
if args.daemon:
|
if args.daemon:
|
||||||
|
@ -35,7 +35,6 @@ ERROR_LOG_FILENAME = 'home-assistant.log'
|
|||||||
|
|
||||||
def setup_component(hass, domain, config=None):
|
def setup_component(hass, domain, config=None):
|
||||||
"""Setup a component and all its dependencies."""
|
"""Setup a component and all its dependencies."""
|
||||||
|
|
||||||
if domain in hass.config.components:
|
if domain in hass.config.components:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -58,7 +57,7 @@ def setup_component(hass, domain, config=None):
|
|||||||
|
|
||||||
|
|
||||||
def _handle_requirements(hass, component, name):
|
def _handle_requirements(hass, component, name):
|
||||||
""" Installs requirements for component. """
|
"""Install the requirements for a component."""
|
||||||
if hass.config.skip_pip or not hasattr(component, 'REQUIREMENTS'):
|
if hass.config.skip_pip or not hasattr(component, 'REQUIREMENTS'):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -126,7 +125,7 @@ def _setup_component(hass, domain, config):
|
|||||||
|
|
||||||
|
|
||||||
def prepare_setup_platform(hass, config, domain, platform_name):
|
def prepare_setup_platform(hass, config, domain, platform_name):
|
||||||
""" Loads a platform and makes sure dependencies are setup. """
|
"""Load a platform and makes sure dependencies are setup."""
|
||||||
_ensure_loader_prepared(hass)
|
_ensure_loader_prepared(hass)
|
||||||
|
|
||||||
platform_path = PLATFORM_FORMAT.format(domain, platform_name)
|
platform_path = PLATFORM_FORMAT.format(domain, platform_name)
|
||||||
@ -158,7 +157,7 @@ def prepare_setup_platform(hass, config, domain, platform_name):
|
|||||||
|
|
||||||
|
|
||||||
def mount_local_lib_path(config_dir):
|
def mount_local_lib_path(config_dir):
|
||||||
""" Add local library to Python Path """
|
"""Add local library to Python Path."""
|
||||||
sys.path.insert(0, os.path.join(config_dir, 'lib'))
|
sys.path.insert(0, os.path.join(config_dir, 'lib'))
|
||||||
|
|
||||||
|
|
||||||
@ -166,8 +165,7 @@ def mount_local_lib_path(config_dir):
|
|||||||
def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
||||||
verbose=False, daemon=False, skip_pip=False,
|
verbose=False, daemon=False, skip_pip=False,
|
||||||
log_rotate_days=None):
|
log_rotate_days=None):
|
||||||
"""
|
"""Try to configure Home Assistant from a config dict.
|
||||||
Tries to configure Home Assistant from a config dict.
|
|
||||||
|
|
||||||
Dynamically loads required components and its dependencies.
|
Dynamically loads required components and its dependencies.
|
||||||
"""
|
"""
|
||||||
@ -209,7 +207,7 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
|||||||
|
|
||||||
_LOGGER.info('Home Assistant core initialized')
|
_LOGGER.info('Home Assistant core initialized')
|
||||||
|
|
||||||
# give event decorators access to HASS
|
# Give event decorators access to HASS
|
||||||
event_decorators.HASS = hass
|
event_decorators.HASS = hass
|
||||||
service.HASS = hass
|
service.HASS = hass
|
||||||
|
|
||||||
@ -222,9 +220,9 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
|||||||
|
|
||||||
def from_config_file(config_path, hass=None, verbose=False, daemon=False,
|
def from_config_file(config_path, hass=None, verbose=False, daemon=False,
|
||||||
skip_pip=True, log_rotate_days=None):
|
skip_pip=True, log_rotate_days=None):
|
||||||
"""
|
"""Read the configuration file and try to start all the functionality.
|
||||||
Reads the configuration file and tries to start all the required
|
|
||||||
functionality. Will add functionality to 'hass' parameter if given,
|
Will add functionality to 'hass' parameter if given,
|
||||||
instantiates a new Home Assistant object if 'hass' is not given.
|
instantiates a new Home Assistant object if 'hass' is not given.
|
||||||
"""
|
"""
|
||||||
if hass is None:
|
if hass is None:
|
||||||
@ -244,7 +242,7 @@ def from_config_file(config_path, hass=None, verbose=False, daemon=False,
|
|||||||
|
|
||||||
|
|
||||||
def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None):
|
def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None):
|
||||||
""" Setup the logging for home assistant. """
|
"""Setup the logging."""
|
||||||
if not daemon:
|
if not daemon:
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
fmt = ("%(log_color)s%(asctime)s %(levelname)s (%(threadName)s) "
|
fmt = ("%(log_color)s%(asctime)s %(levelname)s (%(threadName)s) "
|
||||||
@ -322,11 +320,11 @@ def process_ha_config_upgrade(hass):
|
|||||||
|
|
||||||
|
|
||||||
def process_ha_core_config(hass, config):
|
def process_ha_core_config(hass, config):
|
||||||
""" Processes the [homeassistant] section from the config. """
|
"""Process the [homeassistant] section from the config."""
|
||||||
hac = hass.config
|
hac = hass.config
|
||||||
|
|
||||||
def set_time_zone(time_zone_str):
|
def set_time_zone(time_zone_str):
|
||||||
""" Helper method to set time zone in HA. """
|
"""Helper method to set time zone."""
|
||||||
if time_zone_str is None:
|
if time_zone_str is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
"""
|
"""Module to help with parsing and generating configuration files."""
|
||||||
homeassistant.config
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Module to help with parsing and generating configuration files.
|
|
||||||
"""
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -50,9 +45,11 @@ def get_default_config_dir():
|
|||||||
|
|
||||||
|
|
||||||
def ensure_config_exists(config_dir, detect_location=True):
|
def ensure_config_exists(config_dir, detect_location=True):
|
||||||
""" Ensures a config file exists in given config dir.
|
"""Ensure a config file exists in given configuration directory.
|
||||||
|
|
||||||
Creating a default one if needed.
|
Creating a default one if needed.
|
||||||
Returns path to the config file. """
|
Return path to the config file.
|
||||||
|
"""
|
||||||
config_path = find_config_file(config_dir)
|
config_path = find_config_file(config_dir)
|
||||||
|
|
||||||
if config_path is None:
|
if config_path is None:
|
||||||
@ -64,8 +61,10 @@ def ensure_config_exists(config_dir, detect_location=True):
|
|||||||
|
|
||||||
|
|
||||||
def create_default_config(config_dir, detect_location=True):
|
def create_default_config(config_dir, detect_location=True):
|
||||||
""" Creates a default configuration file in given config dir.
|
"""Create a default configuration file in given configuration directory.
|
||||||
Returns path to new config file if success, None if failed. """
|
|
||||||
|
Return path to new config file if success, None if failed.
|
||||||
|
"""
|
||||||
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
||||||
|
|
||||||
info = {attr: default for attr, default, _, _ in DEFAULT_CONFIG}
|
info = {attr: default for attr, default, _, _ in DEFAULT_CONFIG}
|
||||||
@ -108,7 +107,7 @@ def create_default_config(config_dir, detect_location=True):
|
|||||||
|
|
||||||
|
|
||||||
def find_config_file(config_dir):
|
def find_config_file(config_dir):
|
||||||
""" Looks in given directory for supported config files. """
|
"""Look in given directory for supported configuration files."""
|
||||||
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
||||||
|
|
||||||
return config_path if os.path.isfile(config_path) else None
|
return config_path if os.path.isfile(config_path) else None
|
||||||
|
@ -132,12 +132,13 @@ class JobPriority(util.OrderedEnum):
|
|||||||
|
|
||||||
|
|
||||||
class EventOrigin(enum.Enum):
|
class EventOrigin(enum.Enum):
|
||||||
"""Represents origin of an event."""
|
"""Represent the origin of an event."""
|
||||||
|
|
||||||
local = "LOCAL"
|
local = "LOCAL"
|
||||||
remote = "REMOTE"
|
remote = "REMOTE"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
"""Return the event."""
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
@ -166,6 +167,7 @@ class Event(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""Return the representation."""
|
||||||
# pylint: disable=maybe-no-member
|
# pylint: disable=maybe-no-member
|
||||||
if self.data:
|
if self.data:
|
||||||
return "<Event {}[{}]: {}>".format(
|
return "<Event {}[{}]: {}>".format(
|
||||||
@ -176,6 +178,7 @@ class Event(object):
|
|||||||
str(self.origin)[0])
|
str(self.origin)[0])
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
"""Return the comparison."""
|
||||||
return (self.__class__ == other.__class__ and
|
return (self.__class__ == other.__class__ and
|
||||||
self.event_type == other.event_type and
|
self.event_type == other.event_type and
|
||||||
self.data == other.data and
|
self.data == other.data and
|
||||||
@ -246,7 +249,7 @@ class EventBus(object):
|
|||||||
"""
|
"""
|
||||||
@ft.wraps(listener)
|
@ft.wraps(listener)
|
||||||
def onetime_listener(event):
|
def onetime_listener(event):
|
||||||
"""Remove listener from eventbus and then fires listener."""
|
"""Remove listener from eventbus and then fire listener."""
|
||||||
if hasattr(onetime_listener, 'run'):
|
if hasattr(onetime_listener, 'run'):
|
||||||
return
|
return
|
||||||
# Set variable so that we will never run twice.
|
# Set variable so that we will never run twice.
|
||||||
@ -281,8 +284,7 @@ class EventBus(object):
|
|||||||
|
|
||||||
|
|
||||||
class State(object):
|
class State(object):
|
||||||
"""
|
"""Object to represent a state within the state machine.
|
||||||
Object to represent a state within the state machine.
|
|
||||||
|
|
||||||
entity_id: the entity that is represented.
|
entity_id: the entity that is represented.
|
||||||
state: the state of the entity
|
state: the state of the entity
|
||||||
@ -369,12 +371,14 @@ class State(object):
|
|||||||
json_dict.get('attributes'), last_changed, last_updated)
|
json_dict.get('attributes'), last_changed, last_updated)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
"""Return the comparison of the state."""
|
||||||
return (self.__class__ == other.__class__ and
|
return (self.__class__ == other.__class__ and
|
||||||
self.entity_id == other.entity_id and
|
self.entity_id == other.entity_id and
|
||||||
self.state == other.state and
|
self.state == other.state and
|
||||||
self.attributes == other.attributes)
|
self.attributes == other.attributes)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""Return the representation of the states."""
|
||||||
attr = "; {}".format(util.repr_helper(self.attributes)) \
|
attr = "; {}".format(util.repr_helper(self.attributes)) \
|
||||||
if self.attributes else ""
|
if self.attributes else ""
|
||||||
|
|
||||||
@ -524,6 +528,7 @@ class ServiceCall(object):
|
|||||||
self.call_id = call_id
|
self.call_id = call_id
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""Return the represenation of the service."""
|
||||||
if self.data:
|
if self.data:
|
||||||
return "<ServiceCall {}.{}: {}>".format(
|
return "<ServiceCall {}.{}: {}>".format(
|
||||||
self.domain, self.service, util.repr_helper(self.data))
|
self.domain, self.service, util.repr_helper(self.data))
|
||||||
|
@ -1,23 +1,28 @@
|
|||||||
""" Exceptions used by Home Assistant """
|
"""Exceptions used by Home Assistant."""
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantError(Exception):
|
class HomeAssistantError(Exception):
|
||||||
""" General Home Assistant exception occured. """
|
"""General Home Assistant exception occurred."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class InvalidEntityFormatError(HomeAssistantError):
|
class InvalidEntityFormatError(HomeAssistantError):
|
||||||
"""When an invalid formatted entity is encountered."""
|
"""When an invalid formatted entity is encountered."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NoEntitySpecifiedError(HomeAssistantError):
|
class NoEntitySpecifiedError(HomeAssistantError):
|
||||||
"""When no entity is specified."""
|
"""When no entity is specified."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TemplateError(HomeAssistantError):
|
class TemplateError(HomeAssistantError):
|
||||||
"""Error during template rendering."""
|
"""Error during template rendering."""
|
||||||
|
|
||||||
def __init__(self, exception):
|
def __init__(self, exception):
|
||||||
|
"""Initalize the error."""
|
||||||
super().__init__('{}: {}'.format(exception.__class__.__name__,
|
super().__init__('{}: {}'.format(exception.__class__.__name__,
|
||||||
exception))
|
exception))
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.loader
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Provides methods for loading Home Assistant components.
|
Provides methods for loading Home Assistant components.
|
||||||
|
|
||||||
This module has quite some complex parts. I have tried to add as much
|
This module has quite some complex parts. I have tried to add as much
|
||||||
@ -33,7 +30,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def prepare(hass):
|
def prepare(hass):
|
||||||
""" Prepares the loading of components. """
|
"""Prepare the loading of components."""
|
||||||
global PREPARED # pylint: disable=global-statement
|
global PREPARED # pylint: disable=global-statement
|
||||||
|
|
||||||
# Load the built-in components
|
# Load the built-in components
|
||||||
@ -74,17 +71,18 @@ def prepare(hass):
|
|||||||
|
|
||||||
|
|
||||||
def set_component(comp_name, component):
|
def set_component(comp_name, component):
|
||||||
""" Sets a component in the cache. """
|
"""Set a component in the cache."""
|
||||||
_check_prepared()
|
_check_prepared()
|
||||||
|
|
||||||
_COMPONENT_CACHE[comp_name] = component
|
_COMPONENT_CACHE[comp_name] = component
|
||||||
|
|
||||||
|
|
||||||
def get_component(comp_name):
|
def get_component(comp_name):
|
||||||
""" Tries to load specified component.
|
"""Try to load specified component.
|
||||||
Looks in config dir first, then built-in components.
|
|
||||||
Only returns it if also found to be valid. """
|
|
||||||
|
|
||||||
|
Looks in config dir first, then built-in components.
|
||||||
|
Only returns it if also found to be valid.
|
||||||
|
"""
|
||||||
if comp_name in _COMPONENT_CACHE:
|
if comp_name in _COMPONENT_CACHE:
|
||||||
return _COMPONENT_CACHE[comp_name]
|
return _COMPONENT_CACHE[comp_name]
|
||||||
|
|
||||||
@ -145,8 +143,8 @@ def get_component(comp_name):
|
|||||||
|
|
||||||
|
|
||||||
def load_order_components(components):
|
def load_order_components(components):
|
||||||
"""
|
"""Take in a list of components we want to load.
|
||||||
Takes in a list of components we want to load:
|
|
||||||
- filters out components we cannot load
|
- filters out components we cannot load
|
||||||
- filters out components that have invalid/circular dependencies
|
- filters out components that have invalid/circular dependencies
|
||||||
- Will make sure the recorder component is loaded first
|
- Will make sure the recorder component is loaded first
|
||||||
@ -175,8 +173,8 @@ def load_order_components(components):
|
|||||||
|
|
||||||
|
|
||||||
def load_order_component(comp_name):
|
def load_order_component(comp_name):
|
||||||
"""
|
"""Return an OrderedSet of components in the correct order of loading.
|
||||||
Returns an OrderedSet of components in the correct order of loading.
|
|
||||||
Raises HomeAssistantError if a circular dependency is detected.
|
Raises HomeAssistantError if a circular dependency is detected.
|
||||||
Returns an empty list if component could not be loaded.
|
Returns an empty list if component could not be loaded.
|
||||||
"""
|
"""
|
||||||
@ -187,7 +185,7 @@ def _load_order_component(comp_name, load_order, loading):
|
|||||||
"""Recursive function to get load order of components."""
|
"""Recursive function to get load order of components."""
|
||||||
component = get_component(comp_name)
|
component = get_component(comp_name)
|
||||||
|
|
||||||
# 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 None:
|
if component is None:
|
||||||
return OrderedSet()
|
return OrderedSet()
|
||||||
|
|
||||||
@ -198,7 +196,7 @@ def _load_order_component(comp_name, load_order, loading):
|
|||||||
if dependency in load_order:
|
if dependency in load_order:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# If we are already loading it, we have a circular dependency
|
# If we are already loading it, we have a circular dependency.
|
||||||
if dependency in loading:
|
if dependency in loading:
|
||||||
_LOGGER.error('Circular dependency detected: %s -> %s',
|
_LOGGER.error('Circular dependency detected: %s -> %s',
|
||||||
comp_name, dependency)
|
comp_name, dependency)
|
||||||
@ -221,7 +219,7 @@ def _load_order_component(comp_name, load_order, loading):
|
|||||||
|
|
||||||
|
|
||||||
def _check_prepared():
|
def _check_prepared():
|
||||||
""" Issues a warning if loader.prepare() has never been called. """
|
"""Issue a warning if loader.prepare() has never been called."""
|
||||||
if not PREPARED:
|
if not PREPARED:
|
||||||
_LOGGER.warning((
|
_LOGGER.warning((
|
||||||
"You did not call loader.prepare() yet. "
|
"You did not call loader.prepare() yet. "
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
homeassistant.remote
|
Support for an interface to work with a remote instance of Home Assistant.
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
A module containing drop in replacements for core parts that will interface
|
|
||||||
with a remote instance of Home Assistant.
|
|
||||||
|
|
||||||
If a connection error occurs while communicating with the API a
|
If a connection error occurs while communicating with the API a
|
||||||
HomeAssistantError will be raised.
|
HomeAssistantError will be raised.
|
||||||
@ -34,23 +31,25 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class APIStatus(enum.Enum):
|
class APIStatus(enum.Enum):
|
||||||
""" Represents API status. """
|
"""Represent API status."""
|
||||||
# pylint: disable=no-init,invalid-name,too-few-public-methods
|
|
||||||
|
|
||||||
|
# pylint: disable=no-init,invalid-name,too-few-public-methods
|
||||||
OK = "ok"
|
OK = "ok"
|
||||||
INVALID_PASSWORD = "invalid_password"
|
INVALID_PASSWORD = "invalid_password"
|
||||||
CANNOT_CONNECT = "cannot_connect"
|
CANNOT_CONNECT = "cannot_connect"
|
||||||
UNKNOWN = "unknown"
|
UNKNOWN = "unknown"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
"""Return the state."""
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class API(object):
|
class API(object):
|
||||||
"""Object to pass around Home Assistant API location and credentials."""
|
"""Object to pass around Home Assistant API location and credentials."""
|
||||||
# pylint: disable=too-few-public-methods
|
|
||||||
|
|
||||||
|
# pylint: disable=too-few-public-methods
|
||||||
def __init__(self, host, api_password=None, port=None, use_ssl=False):
|
def __init__(self, host, api_password=None, port=None, use_ssl=False):
|
||||||
|
"""Initalize the API."""
|
||||||
self.host = host
|
self.host = host
|
||||||
self.port = port or SERVER_PORT
|
self.port = port or SERVER_PORT
|
||||||
self.api_password = api_password
|
self.api_password = api_password
|
||||||
@ -65,14 +64,14 @@ class API(object):
|
|||||||
self._headers[HTTP_HEADER_HA_AUTH] = api_password
|
self._headers[HTTP_HEADER_HA_AUTH] = api_password
|
||||||
|
|
||||||
def validate_api(self, force_validate=False):
|
def validate_api(self, force_validate=False):
|
||||||
""" Tests if we can communicate with the API. """
|
"""Test if we can communicate with the API."""
|
||||||
if self.status is None or force_validate:
|
if self.status is None or force_validate:
|
||||||
self.status = validate_api(self)
|
self.status = validate_api(self)
|
||||||
|
|
||||||
return self.status == APIStatus.OK
|
return self.status == APIStatus.OK
|
||||||
|
|
||||||
def __call__(self, method, path, data=None):
|
def __call__(self, method, path, data=None):
|
||||||
""" Makes a call to the Home Assistant API. """
|
"""Make a call to the Home Assistant API."""
|
||||||
if data is not None:
|
if data is not None:
|
||||||
data = json.dumps(data, cls=JSONEncoder)
|
data = json.dumps(data, cls=JSONEncoder)
|
||||||
|
|
||||||
@ -96,15 +95,17 @@ class API(object):
|
|||||||
raise HomeAssistantError(error)
|
raise HomeAssistantError(error)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
"""Return the representation of the API."""
|
||||||
return "API({}, {}, {})".format(
|
return "API({}, {}, {})".format(
|
||||||
self.host, self.api_password, self.port)
|
self.host, self.api_password, self.port)
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistant(ha.HomeAssistant):
|
class HomeAssistant(ha.HomeAssistant):
|
||||||
"""Home Assistant that forwards work."""
|
"""Home Assistant that forwards work."""
|
||||||
# pylint: disable=super-init-not-called,too-many-instance-attributes
|
|
||||||
|
|
||||||
|
# pylint: disable=super-init-not-called,too-many-instance-attributes
|
||||||
def __init__(self, remote_api, local_api=None):
|
def __init__(self, remote_api, local_api=None):
|
||||||
|
"""Initalize the forward instance."""
|
||||||
if not remote_api.validate_api():
|
if not remote_api.validate_api():
|
||||||
raise HomeAssistantError(
|
raise HomeAssistantError(
|
||||||
"Remote API at {}:{} not valid: {}".format(
|
"Remote API at {}:{} not valid: {}".format(
|
||||||
@ -122,6 +123,7 @@ class HomeAssistant(ha.HomeAssistant):
|
|||||||
self.config.api = local_api
|
self.config.api = local_api
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
"""Start the instance."""
|
||||||
# Ensure a local API exists to connect with remote
|
# Ensure a local API exists to connect with remote
|
||||||
if self.config.api is None:
|
if self.config.api is None:
|
||||||
if not bootstrap.setup_component(self, 'api'):
|
if not bootstrap.setup_component(self, 'api'):
|
||||||
@ -141,7 +143,7 @@ class HomeAssistant(ha.HomeAssistant):
|
|||||||
'local api {}').format(self.remote_api, self.config.api))
|
'local api {}').format(self.remote_api, self.config.api))
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
""" Stops Home Assistant and shuts down all threads. """
|
"""Stop Home Assistant and shuts down all threads."""
|
||||||
_LOGGER.info("Stopping")
|
_LOGGER.info("Stopping")
|
||||||
|
|
||||||
self.bus.fire(ha.EVENT_HOMEASSISTANT_STOP,
|
self.bus.fire(ha.EVENT_HOMEASSISTANT_STOP,
|
||||||
@ -155,15 +157,18 @@ class HomeAssistant(ha.HomeAssistant):
|
|||||||
|
|
||||||
class EventBus(ha.EventBus):
|
class EventBus(ha.EventBus):
|
||||||
"""EventBus implementation that forwards fire_event to remote API."""
|
"""EventBus implementation that forwards fire_event to remote API."""
|
||||||
# pylint: disable=too-few-public-methods
|
|
||||||
|
|
||||||
|
# pylint: disable=too-few-public-methods
|
||||||
def __init__(self, api, pool=None):
|
def __init__(self, api, pool=None):
|
||||||
|
"""Initalize the eventbus."""
|
||||||
super().__init__(pool)
|
super().__init__(pool)
|
||||||
self._api = api
|
self._api = api
|
||||||
|
|
||||||
def fire(self, event_type, event_data=None, origin=ha.EventOrigin.local):
|
def fire(self, event_type, event_data=None, origin=ha.EventOrigin.local):
|
||||||
""" Forward local events to remote target,
|
"""Forward local events to remote target.
|
||||||
handles remote event as usual. """
|
|
||||||
|
Handles remote event as usual.
|
||||||
|
"""
|
||||||
# All local events that are not TIME_CHANGED are forwarded to API
|
# All local events that are not TIME_CHANGED are forwarded to API
|
||||||
if origin == ha.EventOrigin.local and \
|
if origin == ha.EventOrigin.local and \
|
||||||
event_type != ha.EVENT_TIME_CHANGED:
|
event_type != ha.EVENT_TIME_CHANGED:
|
||||||
@ -178,6 +183,7 @@ class EventForwarder(object):
|
|||||||
"""Listens for events and forwards to specified APIs."""
|
"""Listens for events and forwards to specified APIs."""
|
||||||
|
|
||||||
def __init__(self, hass, restrict_origin=None):
|
def __init__(self, hass, restrict_origin=None):
|
||||||
|
"""Initalize the event forwarder."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.restrict_origin = restrict_origin
|
self.restrict_origin = restrict_origin
|
||||||
|
|
||||||
@ -188,8 +194,7 @@ class EventForwarder(object):
|
|||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
|
|
||||||
def connect(self, api):
|
def connect(self, api):
|
||||||
"""
|
"""Attach to a Home Assistant instance and forward events.
|
||||||
Attach to a Home Assistant instance and forward events.
|
|
||||||
|
|
||||||
Will overwrite old target if one exists with same host/port.
|
Will overwrite old target if one exists with same host/port.
|
||||||
"""
|
"""
|
||||||
@ -203,7 +208,7 @@ class EventForwarder(object):
|
|||||||
self._targets[key] = api
|
self._targets[key] = api
|
||||||
|
|
||||||
def disconnect(self, api):
|
def disconnect(self, api):
|
||||||
""" Removes target from being forwarded to. """
|
"""Remove target from being forwarded to."""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
key = (api.host, api.port)
|
key = (api.host, api.port)
|
||||||
|
|
||||||
@ -217,7 +222,7 @@ class EventForwarder(object):
|
|||||||
return did_remove
|
return did_remove
|
||||||
|
|
||||||
def _event_listener(self, event):
|
def _event_listener(self, event):
|
||||||
""" Listen and forwards all events. """
|
"""Listen and forward all events."""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
# We don't forward time events or, if enabled, non-local events
|
# We don't forward time events or, if enabled, non-local events
|
||||||
if event.event_type == ha.EVENT_TIME_CHANGED or \
|
if event.event_type == ha.EVENT_TIME_CHANGED or \
|
||||||
@ -229,16 +234,12 @@ class EventForwarder(object):
|
|||||||
|
|
||||||
|
|
||||||
class StateMachine(ha.StateMachine):
|
class StateMachine(ha.StateMachine):
|
||||||
"""
|
"""Fire set events to an API. Uses state_change events to track states."""
|
||||||
Fires set events to an API.
|
|
||||||
Uses state_change events to track states.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, bus, api):
|
def __init__(self, bus, api):
|
||||||
|
"""Initalize the statemachine."""
|
||||||
super().__init__(None)
|
super().__init__(None)
|
||||||
|
|
||||||
self._api = api
|
self._api = api
|
||||||
|
|
||||||
self.mirror()
|
self.mirror()
|
||||||
|
|
||||||
bus.listen(ha.EVENT_STATE_CHANGED, self._state_changed_listener)
|
bus.listen(ha.EVENT_STATE_CHANGED, self._state_changed_listener)
|
||||||
@ -251,16 +252,16 @@ class StateMachine(ha.StateMachine):
|
|||||||
return remove_state(self._api, entity_id)
|
return remove_state(self._api, entity_id)
|
||||||
|
|
||||||
def set(self, entity_id, new_state, attributes=None):
|
def set(self, entity_id, new_state, attributes=None):
|
||||||
""" Calls set_state on remote API . """
|
"""Call set_state on remote API."""
|
||||||
set_state(self._api, entity_id, new_state, attributes)
|
set_state(self._api, entity_id, new_state, attributes)
|
||||||
|
|
||||||
def mirror(self):
|
def mirror(self):
|
||||||
""" Discards current data and mirrors the remote state machine. """
|
"""Discard current data and mirrors the remote state machine."""
|
||||||
self._states = {state.entity_id: state for state
|
self._states = {state.entity_id: state for state
|
||||||
in get_states(self._api)}
|
in get_states(self._api)}
|
||||||
|
|
||||||
def _state_changed_listener(self, event):
|
def _state_changed_listener(self, event):
|
||||||
""" Listens for state changed events and applies them. """
|
"""Listen for state changed events and applies them."""
|
||||||
if event.data['new_state'] is None:
|
if event.data['new_state'] is None:
|
||||||
self._states.pop(event.data['entity_id'], None)
|
self._states.pop(event.data['entity_id'], None)
|
||||||
else:
|
else:
|
||||||
@ -269,11 +270,13 @@ class StateMachine(ha.StateMachine):
|
|||||||
|
|
||||||
class JSONEncoder(json.JSONEncoder):
|
class JSONEncoder(json.JSONEncoder):
|
||||||
"""JSONEncoder that supports Home Assistant objects."""
|
"""JSONEncoder that supports Home Assistant objects."""
|
||||||
# pylint: disable=too-few-public-methods,method-hidden
|
|
||||||
|
|
||||||
|
# pylint: disable=too-few-public-methods,method-hidden
|
||||||
def default(self, obj):
|
def default(self, obj):
|
||||||
""" Converts Home Assistant objects and hands
|
"""Convert Home Assistant objects.
|
||||||
other objects to the original method. """
|
|
||||||
|
Hand other objects to the original method.
|
||||||
|
"""
|
||||||
if hasattr(obj, 'as_dict'):
|
if hasattr(obj, 'as_dict'):
|
||||||
return obj.as_dict()
|
return obj.as_dict()
|
||||||
|
|
||||||
@ -291,7 +294,7 @@ class JSONEncoder(json.JSONEncoder):
|
|||||||
|
|
||||||
|
|
||||||
def validate_api(api):
|
def validate_api(api):
|
||||||
""" Makes a call to validate API. """
|
"""Make a call to validate API."""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_GET, URL_API)
|
req = api(METHOD_GET, URL_API)
|
||||||
|
|
||||||
@ -309,8 +312,7 @@ def validate_api(api):
|
|||||||
|
|
||||||
|
|
||||||
def connect_remote_events(from_api, to_api):
|
def connect_remote_events(from_api, to_api):
|
||||||
""" Sets up from_api to forward all events to to_api. """
|
"""Setup from_api to forward all events to to_api."""
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'host': to_api.host,
|
'host': to_api.host,
|
||||||
'api_password': to_api.api_password,
|
'api_password': to_api.api_password,
|
||||||
@ -335,7 +337,7 @@ def connect_remote_events(from_api, to_api):
|
|||||||
|
|
||||||
|
|
||||||
def disconnect_remote_events(from_api, to_api):
|
def disconnect_remote_events(from_api, to_api):
|
||||||
""" Disconnects forwarding events from from_api to to_api. """
|
"""Disconnect forwarding events from from_api to to_api."""
|
||||||
data = {
|
data = {
|
||||||
'host': to_api.host,
|
'host': to_api.host,
|
||||||
'port': to_api.port
|
'port': to_api.port
|
||||||
@ -374,7 +376,6 @@ def get_event_listeners(api):
|
|||||||
|
|
||||||
def fire_event(api, event_type, data=None):
|
def fire_event(api, event_type, data=None):
|
||||||
"""Fire an event at remote API."""
|
"""Fire an event at remote API."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_POST, URL_API_EVENTS_EVENT.format(event_type), data)
|
req = api(METHOD_POST, URL_API_EVENTS_EVENT.format(event_type), data)
|
||||||
|
|
||||||
@ -387,8 +388,7 @@ def fire_event(api, event_type, data=None):
|
|||||||
|
|
||||||
|
|
||||||
def get_state(api, entity_id):
|
def get_state(api, entity_id):
|
||||||
""" Queries given API for state of entity_id. """
|
"""Query given API for state of entity_id."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_GET, URL_API_STATES_ENTITY.format(entity_id))
|
req = api(METHOD_GET, URL_API_STATES_ENTITY.format(entity_id))
|
||||||
|
|
||||||
@ -405,8 +405,7 @@ def get_state(api, entity_id):
|
|||||||
|
|
||||||
|
|
||||||
def get_states(api):
|
def get_states(api):
|
||||||
""" Queries given API for all states. """
|
"""Query given API for all states."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_GET,
|
req = api(METHOD_GET,
|
||||||
URL_API_STATES)
|
URL_API_STATES)
|
||||||
@ -424,7 +423,7 @@ def get_states(api):
|
|||||||
def remove_state(api, entity_id):
|
def remove_state(api, entity_id):
|
||||||
"""Call API to remove state for entity_id.
|
"""Call API to remove state for entity_id.
|
||||||
|
|
||||||
Returns True if entity is gone (removed/never existed).
|
Return True if entity is gone (removed/never existed).
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_DELETE, URL_API_STATES_ENTITY.format(entity_id))
|
req = api(METHOD_DELETE, URL_API_STATES_ENTITY.format(entity_id))
|
||||||
@ -442,11 +441,10 @@ def remove_state(api, entity_id):
|
|||||||
|
|
||||||
|
|
||||||
def set_state(api, entity_id, new_state, attributes=None):
|
def set_state(api, entity_id, new_state, attributes=None):
|
||||||
"""
|
"""Tell API to update state for entity_id.
|
||||||
Tells API to update state for entity_id.
|
|
||||||
Returns True if success.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
Return True if success.
|
||||||
|
"""
|
||||||
attributes = attributes or {}
|
attributes = attributes or {}
|
||||||
|
|
||||||
data = {'state': new_state,
|
data = {'state': new_state,
|
||||||
@ -471,16 +469,16 @@ def set_state(api, entity_id, new_state, attributes=None):
|
|||||||
|
|
||||||
|
|
||||||
def is_state(api, entity_id, state):
|
def is_state(api, entity_id, state):
|
||||||
""" Queries API to see if entity_id is specified state. """
|
"""Query API to see if entity_id is specified state."""
|
||||||
cur_state = get_state(api, entity_id)
|
cur_state = get_state(api, entity_id)
|
||||||
|
|
||||||
return cur_state and cur_state.state == state
|
return cur_state and cur_state.state == state
|
||||||
|
|
||||||
|
|
||||||
def get_services(api):
|
def get_services(api):
|
||||||
"""
|
"""Return a list of dicts.
|
||||||
Returns a list of dicts. Each dict has a string "domain" and
|
|
||||||
a list of strings "services".
|
Each dict has a string "domain" and a list of strings "services".
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_GET, URL_API_SERVICES)
|
req = api(METHOD_GET, URL_API_SERVICES)
|
||||||
@ -495,7 +493,7 @@ def get_services(api):
|
|||||||
|
|
||||||
|
|
||||||
def call_service(api, domain, service, service_data=None):
|
def call_service(api, domain, service, service_data=None):
|
||||||
""" Calls a service at the remote API. """
|
"""Call a service at the remote API."""
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_POST,
|
req = api(METHOD_POST,
|
||||||
URL_API_SERVICES_SERVICE.format(domain, service),
|
URL_API_SERVICES_SERVICE.format(domain, service),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user