Fix PEP257 issues

This commit is contained in:
Fabian Affolter 2016-03-08 00:06:04 +01:00
parent 4f536ac63d
commit 897b5c668f
8 changed files with 145 additions and 141 deletions

View File

@ -0,0 +1 @@
"""Init file for Home Assistant."""

View File

@ -1,4 +1,4 @@
""" Starts home assistant. """ """Starts home assistant."""
from __future__ import print_function from __future__ import print_function
import argparse import argparse
@ -20,7 +20,7 @@ from homeassistant.const import (
def validate_python(): def validate_python():
""" Validate we're running the right Python version. """ """Validate we're running the right Python version."""
major, minor = sys.version_info[:2] major, minor = sys.version_info[:2]
req_major, req_minor = REQUIRED_PYTHON_VER req_major, req_minor = REQUIRED_PYTHON_VER
@ -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
@ -60,7 +59,7 @@ def ensure_config_path(config_dir):
def ensure_config_file(config_dir): def ensure_config_file(config_dir):
""" Ensure configuration file exists. """ """Ensure configuration file exists."""
config_path = config_util.ensure_config_exists(config_dir) config_path = config_util.ensure_config_exists(config_dir)
if config_path is None: if config_path is None:
@ -71,7 +70,7 @@ def ensure_config_file(config_dir):
def get_arguments(): def get_arguments():
""" Get parsed passed in arguments. """ """Get parsed passed in arguments."""
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Home Assistant: Observe, Control, Automate.") description="Home Assistant: Observe, Control, Automate.")
parser.add_argument('--version', action='version', version=__version__) parser.add_argument('--version', action='version', version=__version__)
@ -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 = {
@ -243,7 +243,7 @@ def setup_and_run_hass(config_dir, args, top_process=False):
if args.open_ui: if args.open_ui:
def open_browser(event): def open_browser(event):
""" Open the webinterface in a browser. """ """Open the webinterface in a browser."""
if hass.config.api is not None: if hass.config.api is not None:
import webbrowser import webbrowser
webbrowser.open(hass.config.api.base_url) webbrowser.open(hass.config.api.base_url)
@ -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:

View File

@ -34,8 +34,7 @@ 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) "
@ -297,7 +295,7 @@ def enable_logging(hass, verbose=False, daemon=False, log_rotate_days=None):
def process_ha_config_upgrade(hass): def process_ha_config_upgrade(hass):
""" Upgrade config if necessary. """ """Upgrade config if necessary."""
version_path = hass.config.path('.HA_VERSION') version_path = hass.config.path('.HA_VERSION')
try: try:
@ -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
@ -397,6 +395,6 @@ def process_ha_core_config(hass, config):
def _ensure_loader_prepared(hass): def _ensure_loader_prepared(hass):
""" Ensure Home Assistant loader is prepared. """ """Ensure Home Assistant loader is prepared."""
if not loader.PREPARED: if not loader.PREPARED:
loader.prepare(hass) loader.prepare(hass)

View File

@ -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
@ -43,16 +38,18 @@ DEFAULT_COMPONENTS = {
def get_default_config_dir(): def get_default_config_dir():
""" Put together the default configuration directory based on OS. """ """Put together the default configuration directory based on OS."""
data_dir = os.getenv('APPDATA') if os.name == "nt" \ data_dir = os.getenv('APPDATA') if os.name == "nt" \
else os.path.expanduser('~') else os.path.expanduser('~')
return os.path.join(data_dir, CONFIG_DIR_NAME) return os.path.join(data_dir, CONFIG_DIR_NAME)
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.
Returns path to the config file. """ Creating a default one if needed.
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,14 +107,14 @@ 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
def load_yaml_config_file(config_path): def load_yaml_config_file(config_path):
""" Parse a YAML configuration file. """ """Parse a YAML configuration file."""
conf_dict = load_yaml(config_path) conf_dict = load_yaml(config_path)
if not isinstance(conf_dict, dict): if not isinstance(conf_dict, dict):

View File

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

View File

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

View File

@ -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,14 +143,14 @@ 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
- Will ensure that all components that do not directly depend on - Will ensure that all components that do not directly depend on
the group component will be loaded before the group component. the group component will be loaded before the group component.
- returns an OrderedSet load order. - returns an OrderedSet load order.
""" """
_check_prepared() _check_prepared()
@ -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.
""" """
@ -184,10 +182,10 @@ def load_order_component(comp_name):
def _load_order_component(comp_name, load_order, loading): 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. "

View File

@ -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,
@ -154,16 +156,19 @@ 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:
@ -175,9 +180,10 @@ class EventBus(ha.EventBus):
class EventForwarder(object): 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:
@ -268,12 +269,14 @@ 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
@ -359,7 +361,7 @@ def disconnect_remote_events(from_api, to_api):
def get_event_listeners(api): def get_event_listeners(api):
""" List of events that is being listened for. """ """List of events that is being listened for."""
try: try:
req = api(METHOD_GET, URL_API_EVENTS) req = api(METHOD_GET, URL_API_EVENTS)
@ -373,8 +375,7 @@ 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),