Merge branch 'pep257-utils' into dev

This commit is contained in:
Fabian Affolter 2016-03-08 08:34:50 +01:00
commit 7f1e181c9b
22 changed files with 301 additions and 333 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
import argparse
@ -20,7 +20,7 @@ from homeassistant.const import (
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]
req_major, req_minor = REQUIRED_PYTHON_VER
@ -31,8 +31,7 @@ def validate_python():
def ensure_config_path(config_dir):
""" Validates configuration directory. """
"""Validate the configuration directory."""
lib_dir = os.path.join(config_dir, 'lib')
# Test if configuration directory exists
@ -60,7 +59,7 @@ def ensure_config_path(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)
if config_path is None:
@ -71,7 +70,7 @@ def ensure_config_file(config_dir):
def get_arguments():
""" Get parsed passed in arguments. """
"""Get parsed passed in arguments."""
parser = argparse.ArgumentParser(
description="Home Assistant: Observe, Control, Automate.")
parser.add_argument('--version', action='version', version=__version__)
@ -136,25 +135,25 @@ def get_arguments():
def daemonize():
""" Move current process to daemon process """
# create first fork
"""Move current process to daemon process."""
# Create first fork
pid = os.fork()
if pid > 0:
sys.exit(0)
# decouple fork
# Decouple fork
os.setsid()
os.umask(0)
# create second fork
# Create second fork
pid = os.fork()
if pid > 0:
sys.exit(0)
def check_pid(pid_file):
""" Check that HA is not already running """
# check pid file
"""Check that HA is not already running."""
# Check pid file
try:
pid = int(open(pid_file, 'r').readline())
except IOError:
@ -171,7 +170,7 @@ def check_pid(pid_file):
def write_pid(pid_file):
""" Create PID File """
"""Create a PID File."""
pid = os.getpid()
try:
open(pid_file, 'w').write(str(pid))
@ -181,7 +180,7 @@ def write_pid(pid_file):
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:
hass_path = inp.read().strip()
@ -213,7 +212,7 @@ def install_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")
os.popen('launchctl unload ' + path)
@ -221,9 +220,10 @@ def uninstall_osx():
def setup_and_run_hass(config_dir, args, top_process=False):
"""
Setup HASS and run. Block until stopped. Will assume it is running in a
subprocess unless top_process is set to true.
"""Setup HASS and run.
Block until stopped. Will assume it is running in a subprocess unless
top_process is set to true.
"""
if args.demo_mode:
config = {
@ -243,7 +243,7 @@ def setup_and_run_hass(config_dir, args, top_process=False):
if args.open_ui:
def open_browser(event):
""" Open the webinterface in a browser. """
"""Open the webinterface in a browser."""
if hass.config.api is not None:
import webbrowser
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):
""" 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()
hass_proc.daemon = True
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()
hass_proc.terminate()
@ -289,7 +289,7 @@ def run_hass_process(hass_proc):
def main():
""" Starts Home Assistant. """
"""Start Home Assistant."""
validate_python()
args = get_arguments()
@ -297,7 +297,7 @@ def main():
config_dir = os.path.join(os.getcwd(), args.config)
ensure_config_path(config_dir)
# os x launchd functions
# OS X launchd functions
if args.install_osx:
install_osx()
return 0
@ -311,7 +311,7 @@ def main():
install_osx()
return 0
# daemon functions
# Daemon functions
if args.pid_file:
check_pid(args.pid_file)
if args.daemon:

View File

@ -34,8 +34,7 @@ ERROR_LOG_FILENAME = 'home-assistant.log'
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:
return True
@ -58,7 +57,7 @@ def setup_component(hass, domain, config=None):
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'):
return True
@ -126,7 +125,7 @@ def _setup_component(hass, domain, config):
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)
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):
""" Add local library to Python Path """
"""Add local library to Python Path."""
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,
verbose=False, daemon=False, skip_pip=False,
log_rotate_days=None):
"""
Tries to configure Home Assistant from a config dict.
"""Try to configure Home Assistant from a config dict.
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')
# give event decorators access to HASS
# Give event decorators access to HASS
event_decorators.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,
skip_pip=True, log_rotate_days=None):
"""
Reads the configuration file and tries to start all the required
functionality. Will add functionality to 'hass' parameter if given,
"""Read the configuration file and try to start all the functionality.
Will add functionality to 'hass' parameter if given,
instantiates a new Home Assistant object if 'hass' is not given.
"""
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):
""" Setup the logging for home assistant. """
"""Setup the logging."""
if not daemon:
logging.basicConfig(level=logging.INFO)
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):
""" Upgrade config if necessary. """
"""Upgrade config if necessary."""
version_path = hass.config.path('.HA_VERSION')
try:
@ -322,11 +320,11 @@ def process_ha_config_upgrade(hass):
def process_ha_core_config(hass, config):
""" Processes the [homeassistant] section from the config. """
"""Process the [homeassistant] section from the config."""
hac = hass.config
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:
return
@ -397,6 +395,6 @@ def process_ha_core_config(hass, config):
def _ensure_loader_prepared(hass):
""" Ensure Home Assistant loader is prepared. """
"""Ensure Home Assistant loader is prepared."""
if not loader.PREPARED:
loader.prepare(hass)

View File

@ -1,9 +1,4 @@
"""
homeassistant.config
~~~~~~~~~~~~~~~~~~~~
Module to help with parsing and generating configuration files.
"""
"""Module to help with parsing and generating configuration files."""
import logging
import os
@ -43,16 +38,18 @@ DEFAULT_COMPONENTS = {
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" \
else os.path.expanduser('~')
return os.path.join(data_dir, CONFIG_DIR_NAME)
def ensure_config_exists(config_dir, detect_location=True):
""" Ensures a config file exists in given config dir.
Creating a default one if needed.
Returns path to the config file. """
"""Ensure a config file exists in given configuration directory.
Creating a default one if needed.
Return path to the config file.
"""
config_path = find_config_file(config_dir)
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):
""" Creates a default configuration file in given config dir.
Returns path to new config file if success, None if failed. """
"""Create a default configuration file in given configuration directory.
Return path to new config file if success, None if failed.
"""
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
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):
""" 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)
return config_path if os.path.isfile(config_path) else None
def load_yaml_config_file(config_path):
""" Parse a YAML configuration file. """
"""Parse a YAML configuration file."""
conf_dict = load_yaml(config_path)
if not isinstance(conf_dict, dict):

View File

@ -132,12 +132,13 @@ class JobPriority(util.OrderedEnum):
class EventOrigin(enum.Enum):
"""Represents origin of an event."""
"""Represent the origin of an event."""
local = "LOCAL"
remote = "REMOTE"
def __str__(self):
"""Return the event."""
return self.value
@ -166,6 +167,7 @@ class Event(object):
}
def __repr__(self):
"""Return the representation."""
# pylint: disable=maybe-no-member
if self.data:
return "<Event {}[{}]: {}>".format(
@ -176,6 +178,7 @@ class Event(object):
str(self.origin)[0])
def __eq__(self, other):
"""Return the comparison."""
return (self.__class__ == other.__class__ and
self.event_type == other.event_type and
self.data == other.data and
@ -246,7 +249,7 @@ class EventBus(object):
"""
@ft.wraps(listener)
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'):
return
# Set variable so that we will never run twice.
@ -281,8 +284,7 @@ class EventBus(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.
state: the state of the entity
@ -369,12 +371,14 @@ class State(object):
json_dict.get('attributes'), last_changed, last_updated)
def __eq__(self, other):
"""Return the comparison of the state."""
return (self.__class__ == other.__class__ and
self.entity_id == other.entity_id and
self.state == other.state and
self.attributes == other.attributes)
def __repr__(self):
"""Return the representation of the states."""
attr = "; {}".format(util.repr_helper(self.attributes)) \
if self.attributes else ""
@ -524,6 +528,7 @@ class ServiceCall(object):
self.call_id = call_id
def __repr__(self):
"""Return the represenation of the service."""
if self.data:
return "<ServiceCall {}.{}: {}>".format(
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):
""" General Home Assistant exception occured. """
"""General Home Assistant exception occurred."""
pass
class InvalidEntityFormatError(HomeAssistantError):
""" When an invalid formatted entity is encountered. """
"""When an invalid formatted entity is encountered."""
pass
class NoEntitySpecifiedError(HomeAssistantError):
""" When no entity is specified. """
"""When no entity is specified."""
pass
class TemplateError(HomeAssistantError):
""" Error during template rendering. """
"""Error during template rendering."""
def __init__(self, exception):
"""Initalize the error."""
super().__init__('{}: {}'.format(exception.__class__.__name__,
exception))

View File

@ -1,20 +1,17 @@
"""
Helper methods for components within Home Assistant.
"""
"""Helper methods for components within Home Assistant."""
import re
from homeassistant.const import CONF_PLATFORM
def validate_config(config, items, logger):
"""
Validates if all items are available in the configuration.
"""Validate if all items are available in the configuration.
config is the general dictionary with all the configurations.
items is a dict with per domain which attributes we require.
logger is the logger from the caller to log the errors to.
Returns True if all required items were found.
Return True if all required items were found.
"""
errors_found = False
for domain in items.keys():
@ -33,8 +30,8 @@ def validate_config(config, items, logger):
def config_per_platform(config, domain, logger):
"""
Generator to break a component config into different platforms.
"""Generator to break a component config into different platforms.
For example, will find 'switch', 'switch 2', 'switch 3', .. etc
"""
config_key = domain
@ -59,6 +56,6 @@ def config_per_platform(config, domain, logger):
def extract_domain_configs(config, domain):
""" Extract keys from config for given domain name. """
"""Extract keys from config for given domain name."""
pattern = re.compile(r'^{}(| .+)$'.format(domain))
return [key for key in config.keys() if pattern.match(key)]

View File

@ -1,8 +1,4 @@
"""
homeassistant.helpers.entity.
Provides ABC for entities in HA.
"""
"""An abstract class for entities in HA."""
import re
from collections import defaultdict
@ -49,15 +45,12 @@ class Entity(object):
"""ABC for Home Assistant entities."""
# pylint: disable=no-self-use
# SAFE TO OVERWRITE
# The properties and methods here are safe to overwrite when inherting this
# class. These may be used to customize the behavior of the entity.
@property
def should_poll(self):
"""
Return True if entity has to be polled for state.
"""Return True if entity has to be polled for state.
False if entity pushes its state to HA.
"""
@ -80,8 +73,7 @@ class Entity(object):
@property
def state_attributes(self):
"""
Return the state attributes.
"""Return the state attributes.
Implemented by component base class.
"""
@ -89,8 +81,7 @@ class Entity(object):
@property
def device_state_attributes(self):
"""
Return device specific state attributes.
"""Return device specific state attributes.
Implemented by platform classes.
"""
@ -140,8 +131,7 @@ class Entity(object):
hass = None
def update_ha_state(self, force_refresh=False):
"""
Update Home Assistant with current state of entity.
"""Update Home Assistant with current state of entity.
If force_refresh == True will update entity before setting state.
"""
@ -176,10 +166,10 @@ class Entity(object):
self._attr_setter('hidden', bool, ATTR_HIDDEN, attr)
self._attr_setter('assumed_state', bool, ATTR_ASSUMED_STATE, attr)
# overwrite properties that have been set in the config file
# Overwrite properties that have been set in the config file.
attr.update(_OVERWRITE.get(self.entity_id, {}))
# remove hidden property if false so it won't show up
# Remove hidden property if false so it won't show up.
if not attr.get(ATTR_HIDDEN, True):
attr.pop(ATTR_HIDDEN)
@ -210,16 +200,17 @@ class Entity(object):
pass
def __eq__(self, other):
"""Return the comparison."""
return (isinstance(other, Entity) and
other.unique_id == self.unique_id)
def __repr__(self):
"""Return the representation."""
return "<Entity {}: {}>".format(self.name, self.state)
@staticmethod
def overwrite_attribute(entity_id, attrs, vals):
"""
Overwrite any attribute of an entity.
"""Overwrite any attribute of an entity.
This function should receive a list of attributes and a
list of values. Set attribute to None to remove any overwritten
@ -236,7 +227,6 @@ class ToggleEntity(Entity):
"""ABC for entities that can be turned on and off."""
# pylint: disable=no-self-use
@property
def state(self):
"""Return the state."""
@ -244,7 +234,7 @@ class ToggleEntity(Entity):
@property
def is_on(self):
"""True if entity is on."""
"""Return True if entity is on."""
return False
def turn_on(self, **kwargs):

View File

@ -41,8 +41,7 @@ class EntityComponent(object):
self.scan_interval).add_entities
def setup(self, config):
"""
Set up a full entity component.
"""Set up a full entity component.
Loads the platforms from the config and will listen for supported
discovered platforms.
@ -63,8 +62,7 @@ class EntityComponent(object):
info))
def extract_from_service(self, service):
"""
Extract all known entities from a service call.
"""Extract all known entities from a service call.
Will return all entities if no entities specified in call.
Will return an empty list if entities specified but unknown.
@ -134,6 +132,7 @@ class EntityPlatform(object):
# pylint: disable=too-few-public-methods
def __init__(self, component, scan_interval):
"""Initalize the entity platform."""
self.component = component
self.scan_interval = scan_interval
self.platform_entities = []

View File

@ -1,6 +1,4 @@
"""
Helpers for listening to events
"""
"""Helpers for listening to events."""
import functools as ft
from datetime import timedelta
@ -11,8 +9,8 @@ from ..util import dt as dt_util
def track_state_change(hass, entity_ids, action, from_state=None,
to_state=None):
"""
Track specific state changes.
"""Track specific state changes.
entity_ids, from_state and to_state can be string or list.
Use list to match multiple.
@ -30,7 +28,7 @@ def track_state_change(hass, entity_ids, action, from_state=None,
@ft.wraps(action)
def state_change_listener(event):
""" The listener that listens for specific state changes. """
"""The listener that listens for specific state changes."""
if event.data['entity_id'] not in entity_ids:
return
@ -55,29 +53,25 @@ def track_state_change(hass, entity_ids, action, from_state=None,
def track_point_in_time(hass, action, point_in_time):
"""
Adds a listener that fires once after a spefic point in time.
"""
"""Add a listener that fires once after a spefic point in time."""
utc_point_in_time = dt_util.as_utc(point_in_time)
@ft.wraps(action)
def utc_converter(utc_now):
""" Converts passed in UTC now to local now. """
"""Convert passed in UTC now to local now."""
action(dt_util.as_local(utc_now))
return track_point_in_utc_time(hass, utc_converter, utc_point_in_time)
def track_point_in_utc_time(hass, action, point_in_time):
"""
Adds a listener that fires once after a specific point in UTC time.
"""
"""Add a listener that fires once after a specific point in UTC time."""
# Ensure point_in_time is UTC
point_in_time = dt_util.as_utc(point_in_time)
@ft.wraps(action)
def point_in_time_listener(event):
""" Listens for matching time_changed events. """
"""Listen for matching time_changed events."""
now = event.data[ATTR_NOW]
if now >= point_in_time and \
@ -100,14 +94,12 @@ def track_point_in_utc_time(hass, action, point_in_time):
def track_sunrise(hass, action, offset=None):
"""
Adds a listener that will fire a specified offset from sunrise daily.
"""
"""Add a listener that will fire a specified offset from sunrise daily."""
from homeassistant.components import sun
offset = offset or timedelta()
def next_rise():
""" Returns next sunrise. """
"""Return the next sunrise."""
next_time = sun.next_rising_utc(hass) + offset
while next_time < dt_util.utcnow():
@ -116,7 +108,7 @@ def track_sunrise(hass, action, offset=None):
return next_time
def sunrise_automation_listener(now):
""" Called when it's time for action. """
"""Called when it's time for action."""
track_point_in_utc_time(hass, sunrise_automation_listener, next_rise())
action()
@ -124,14 +116,12 @@ def track_sunrise(hass, action, offset=None):
def track_sunset(hass, action, offset=None):
"""
Adds a listener that will fire a specified offset from sunset daily.
"""
"""Add a listener that will fire a specified offset from sunset daily."""
from homeassistant.components import sun
offset = offset or timedelta()
def next_set():
""" Returns next sunrise. """
"""Return next sunrise."""
next_time = sun.next_setting_utc(hass) + offset
while next_time < dt_util.utcnow():
@ -140,7 +130,7 @@ def track_sunset(hass, action, offset=None):
return next_time
def sunset_automation_listener(now):
""" Called when it's time for action. """
"""Called when it's time for action."""
track_point_in_utc_time(hass, sunset_automation_listener, next_set())
action()
@ -150,13 +140,13 @@ def track_sunset(hass, action, offset=None):
# pylint: disable=too-many-arguments
def track_utc_time_change(hass, action, year=None, month=None, day=None,
hour=None, minute=None, second=None, local=False):
""" Adds a listener that will fire if time matches a pattern. """
"""Add a listener that will fire if time matches a pattern."""
# We do not have to wrap the function with time pattern matching logic
# if no pattern given
if all(val is None for val in (year, month, day, hour, minute, second)):
@ft.wraps(action)
def time_change_listener(event):
""" Fires every time event that comes in. """
"""Fire every time event that comes in."""
action(event.data[ATTR_NOW])
hass.bus.listen(EVENT_TIME_CHANGED, time_change_listener)
@ -168,7 +158,7 @@ def track_utc_time_change(hass, action, year=None, month=None, day=None,
@ft.wraps(action)
def pattern_time_change_listener(event):
""" Listens for matching time_changed events. """
"""Listen for matching time_changed events."""
now = event.data[ATTR_NOW]
if local:
@ -192,13 +182,13 @@ def track_utc_time_change(hass, action, year=None, month=None, day=None,
# pylint: disable=too-many-arguments
def track_time_change(hass, action, year=None, month=None, day=None,
hour=None, minute=None, second=None):
""" Adds a listener that will fire if UTC time matches a pattern. """
"""Add a listener that will fire if UTC time matches a pattern."""
track_utc_time_change(hass, action, year, month, day, hour, minute, second,
local=True)
def _process_match_param(parameter):
""" Wraps parameter in a tuple if it is not one and returns it. """
"""Wrap parameter in a tuple if it is not one and returns it."""
if parameter is None or parameter == MATCH_ALL:
return MATCH_ALL
elif isinstance(parameter, str) and parameter.startswith('/'):
@ -210,7 +200,7 @@ def _process_match_param(parameter):
def _matcher(subject, pattern):
""" Returns True if subject matches the pattern.
"""Return True if subject matches the pattern.
Pattern is either a tuple of allowed subjects or a `MATCH_ALL`.
"""

View File

@ -1,5 +1,4 @@
""" Event Decorators for custom components """
"""Event Decorators for custom components."""
import functools
from homeassistant.helpers import event
@ -8,10 +7,9 @@ HASS = None
def track_state_change(entity_ids, from_state=None, to_state=None):
""" Decorator factory to track state changes for entity id """
"""Decorator factory to track state changes for entity id."""
def track_state_change_decorator(action):
""" Decorator to track state changes """
"""Decorator to track state changes."""
event.track_state_change(HASS, entity_ids,
functools.partial(action, HASS),
from_state, to_state)
@ -21,10 +19,9 @@ def track_state_change(entity_ids, from_state=None, to_state=None):
def track_sunrise(offset=None):
""" Decorator factory to track sunrise events """
"""Decorator factory to track sunrise events."""
def track_sunrise_decorator(action):
""" Decorator to track sunrise events """
"""Decorator to track sunrise events."""
event.track_sunrise(HASS,
functools.partial(action, HASS),
offset)
@ -34,10 +31,9 @@ def track_sunrise(offset=None):
def track_sunset(offset=None):
""" Decorator factory to track sunset events """
"""Decorator factory to track sunset events."""
def track_sunset_decorator(action):
""" Decorator to track sunset events """
"""Decorator to track sunset events."""
event.track_sunset(HASS,
functools.partial(action, HASS),
offset)
@ -49,10 +45,9 @@ def track_sunset(offset=None):
# pylint: disable=too-many-arguments
def track_time_change(year=None, month=None, day=None, hour=None, minute=None,
second=None):
""" Decorator factory to track time changes """
"""Decorator factory to track time changes."""
def track_time_change_decorator(action):
""" Decorator to track time changes """
"""Decorator to track time changes."""
event.track_time_change(HASS,
functools.partial(action, HASS),
year, month, day, hour, minute, second)
@ -64,10 +59,9 @@ def track_time_change(year=None, month=None, day=None, hour=None, minute=None,
# pylint: disable=too-many-arguments
def track_utc_time_change(year=None, month=None, day=None, hour=None,
minute=None, second=None):
""" Decorator factory to track time changes """
"""Decorator factory to track time changes."""
def track_utc_time_change_decorator(action):
""" Decorator to track time changes """
"""Decorator to track time changes."""
event.track_utc_time_change(HASS,
functools.partial(action, HASS),
year, month, day, hour, minute, second)

View File

@ -16,10 +16,9 @@ _LOGGER = logging.getLogger(__name__)
def service(domain, service_name):
""" Decorator factory to register a service """
"""Decorator factory to register a service."""
def register_service_decorator(action):
""" Decorator to register a service """
"""Decorator to register a service."""
HASS.services.register(domain, service_name,
functools.partial(action, HASS))
return action
@ -60,8 +59,8 @@ def call_from_config(hass, config, blocking=False):
def extract_entity_ids(hass, service_call):
"""
Helper method to extract a list of entity ids from a service call.
"""Helper method to extract a list of entity ids from a service call.
Will convert group entity ids to the entity ids it represents.
"""
if not (service_call.data and ATTR_ENTITY_ID in service_call.data):

View File

@ -18,10 +18,10 @@ _LOGGER = logging.getLogger(__name__)
# pylint: disable=too-few-public-methods, attribute-defined-outside-init
class TrackStates(object):
"""
Records the time when the with-block is entered. Will add all states
that have changed since the start time to the return list when with-block
is exited.
"""Record the time when the with-block is entered.
Will add all states that have changed since the start time to the return
list when with-block is exited.
"""
def __init__(self, hass):
@ -100,7 +100,6 @@ def state_as_number(state):
Raises ValueError if this is not possible.
"""
if state.state in (STATE_ON, STATE_LOCKED, STATE_ABOVE_HORIZON,
STATE_OPEN):
return 1

View File

@ -1,16 +1,10 @@
"""
homeassistant.helpers.temperature
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Methods to help handle temperature in Home Assistant.
"""
"""Methods to help handle temperature in Home Assistant."""
import homeassistant.util.temperature as temp_util
from homeassistant.const import TEMP_CELCIUS
def convert(temperature, unit, to_unit):
""" Converts temperature to correct unit. """
"""Convert temperature to correct unit."""
if unit == to_unit or unit is None or to_unit is None:
return temperature
elif unit == TEMP_CELCIUS:

View File

@ -1,6 +1,4 @@
"""
Template helper methods for rendering strings with HA data.
"""
"""Template helper methods for rendering strings with HA data."""
# pylint: disable=too-few-public-methods
import json
import logging
@ -21,8 +19,8 @@ _SENTINEL = object()
def render_with_possible_json_value(hass, template, value,
error_value=_SENTINEL):
"""
Renders template with value exposed.
"""Render template with value exposed.
If valid JSON will expose value_json too.
"""
variables = {
@ -65,17 +63,22 @@ def render(hass, template, variables=None, **kwargs):
class AllStates(object):
"""Class to expose all HA states as attributes."""
def __init__(self, hass):
"""Initialize all states."""
self._hass = hass
def __getattr__(self, name):
"""Return the domain state."""
return DomainStates(self._hass, name)
def __iter__(self):
"""Return all states."""
return iter(sorted(self._hass.states.all(),
key=lambda state: state.entity_id))
def __call__(self, entity_id):
"""Return the states."""
state = self._hass.states.get(entity_id)
return STATE_UNKNOWN if state is None else state.state
@ -84,13 +87,16 @@ class DomainStates(object):
"""Class to expose a specific HA domain as attributes."""
def __init__(self, hass, domain):
"""Initialize the domain states."""
self._hass = hass
self._domain = domain
def __getattr__(self, name):
"""Return the states."""
return self._hass.states.get('{}.{}'.format(self._domain, name))
def __iter__(self):
"""Return the iteration over all the states."""
return iter(sorted(
(state for state in self._hass.states.all()
if state.domain == self._domain),
@ -101,7 +107,7 @@ class LocationMethods(object):
"""Class to expose distance helpers to templates."""
def __init__(self, hass):
"""Initialize distance helpers."""
"""Initialize the distance helpers."""
self._hass = hass
def closest(self, *args):
@ -118,7 +124,6 @@ class LocationMethods(object):
closest('zone.school', 'group.children')
closest(states.zone.school, 'group.children')
"""
if len(args) == 1:
latitude = self._hass.config.latitude
longitude = self._hass.config.longitude
@ -250,8 +255,7 @@ def forgiving_float(value):
class TemplateEnvironment(ImmutableSandboxedEnvironment):
"""Home Assistant template environment."""
"""The Home Assistant template environment."""
def is_safe_callable(self, obj):
"""Test if callback is safe."""
return isinstance(obj, AllStates) or super().is_safe_callable(obj)

View File

@ -1,7 +1,4 @@
"""
homeassistant.loader
~~~~~~~~~~~~~~~~~~~~
Provides methods for loading Home Assistant components.
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):
""" Prepares the loading of components. """
"""Prepare the loading of components."""
global PREPARED # pylint: disable=global-statement
# Load the built-in components
@ -74,17 +71,18 @@ def prepare(hass):
def set_component(comp_name, component):
""" Sets a component in the cache. """
"""Set a component in the cache."""
_check_prepared()
_COMPONENT_CACHE[comp_name] = component
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. """
"""Try 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]
@ -145,14 +143,14 @@ def get_component(comp_name):
def load_order_components(components):
"""
Takes in a list of components we want to load:
- filters out components we cannot load
- filters out components that have invalid/circular dependencies
- Will make sure the recorder component is loaded first
- Will ensure that all components that do not directly depend on
the group component will be loaded before the group component.
- returns an OrderedSet load order.
"""Take in a list of components we want to load.
- filters out components we cannot load
- filters out components that have invalid/circular dependencies
- Will make sure the recorder component is loaded first
- Will ensure that all components that do not directly depend on
the group component will be loaded before the group component.
- returns an OrderedSet load order.
"""
_check_prepared()
@ -175,8 +173,8 @@ def load_order_components(components):
def load_order_component(comp_name):
"""
Returns an OrderedSet of components in the correct order of loading.
"""Return an OrderedSet of components in the correct order of loading.
Raises HomeAssistantError if a circular dependency is detected.
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):
""" Recursive function to get load order of components. """
"""Recursive function to get load order of components."""
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:
return OrderedSet()
@ -198,7 +196,7 @@ def _load_order_component(comp_name, load_order, loading):
if dependency in load_order:
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:
_LOGGER.error('Circular dependency detected: %s -> %s',
comp_name, dependency)
@ -221,7 +219,7 @@ def _load_order_component(comp_name, load_order, loading):
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:
_LOGGER.warning((
"You did not call loader.prepare() yet. "

View File

@ -1,8 +1,5 @@
"""
homeassistant.remote
~~~~~~~~~~~~~~~~~~~~
A module containing drop in replacements for core parts that will interface
with a remote instance of Home Assistant.
Support for an interface to work with a remote instance of Home Assistant.
If a connection error occurs while communicating with the API a
HomeAssistantError will be raised.
@ -34,23 +31,25 @@ _LOGGER = logging.getLogger(__name__)
class APIStatus(enum.Enum):
""" Represents API status. """
# pylint: disable=no-init,invalid-name,too-few-public-methods
"""Represent API status."""
# pylint: disable=no-init,invalid-name,too-few-public-methods
OK = "ok"
INVALID_PASSWORD = "invalid_password"
CANNOT_CONNECT = "cannot_connect"
UNKNOWN = "unknown"
def __str__(self):
"""Return the state."""
return self.value
class API(object):
""" Object to pass around Home Assistant API location and credentials. """
# pylint: disable=too-few-public-methods
"""Object to pass around Home Assistant API location and credentials."""
# pylint: disable=too-few-public-methods
def __init__(self, host, api_password=None, port=None, use_ssl=False):
"""Initalize the API."""
self.host = host
self.port = port or SERVER_PORT
self.api_password = api_password
@ -65,14 +64,14 @@ class API(object):
self._headers[HTTP_HEADER_HA_AUTH] = api_password
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:
self.status = validate_api(self)
return self.status == APIStatus.OK
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:
data = json.dumps(data, cls=JSONEncoder)
@ -96,15 +95,17 @@ class API(object):
raise HomeAssistantError(error)
def __repr__(self):
"""Return the representation of the API."""
return "API({}, {}, {})".format(
self.host, self.api_password, self.port)
class HomeAssistant(ha.HomeAssistant):
""" Home Assistant that forwards work. """
# pylint: disable=super-init-not-called,too-many-instance-attributes
"""Home Assistant that forwards work."""
# pylint: disable=super-init-not-called,too-many-instance-attributes
def __init__(self, remote_api, local_api=None):
"""Initalize the forward instance."""
if not remote_api.validate_api():
raise HomeAssistantError(
"Remote API at {}:{} not valid: {}".format(
@ -122,6 +123,7 @@ class HomeAssistant(ha.HomeAssistant):
self.config.api = local_api
def start(self):
"""Start the instance."""
# Ensure a local API exists to connect with remote
if self.config.api is None:
if not bootstrap.setup_component(self, 'api'):
@ -141,7 +143,7 @@ class HomeAssistant(ha.HomeAssistant):
'local api {}').format(self.remote_api, self.config.api))
def stop(self):
""" Stops Home Assistant and shuts down all threads. """
"""Stop Home Assistant and shuts down all threads."""
_LOGGER.info("Stopping")
self.bus.fire(ha.EVENT_HOMEASSISTANT_STOP,
@ -154,16 +156,19 @@ class HomeAssistant(ha.HomeAssistant):
class EventBus(ha.EventBus):
""" EventBus implementation that forwards fire_event to remote API. """
# pylint: disable=too-few-public-methods
"""EventBus implementation that forwards fire_event to remote API."""
# pylint: disable=too-few-public-methods
def __init__(self, api, pool=None):
"""Initalize the eventbus."""
super().__init__(pool)
self._api = api
def fire(self, event_type, event_data=None, origin=ha.EventOrigin.local):
""" Forward local events to remote target,
handles remote event as usual. """
"""Forward local events to remote target.
Handles remote event as usual.
"""
# All local events that are not TIME_CHANGED are forwarded to API
if origin == ha.EventOrigin.local and \
event_type != ha.EVENT_TIME_CHANGED:
@ -175,9 +180,10 @@ class EventBus(ha.EventBus):
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):
"""Initalize the event forwarder."""
self.hass = hass
self.restrict_origin = restrict_origin
@ -188,8 +194,7 @@ class EventForwarder(object):
self._lock = threading.Lock()
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.
"""
@ -203,7 +208,7 @@ class EventForwarder(object):
self._targets[key] = api
def disconnect(self, api):
""" Removes target from being forwarded to. """
"""Remove target from being forwarded to."""
with self._lock:
key = (api.host, api.port)
@ -217,7 +222,7 @@ class EventForwarder(object):
return did_remove
def _event_listener(self, event):
""" Listen and forwards all events. """
"""Listen and forward all events."""
with self._lock:
# We don't forward time events or, if enabled, non-local events
if event.event_type == ha.EVENT_TIME_CHANGED or \
@ -229,16 +234,12 @@ class EventForwarder(object):
class StateMachine(ha.StateMachine):
"""
Fires set events to an API.
Uses state_change events to track states.
"""
"""Fire set events to an API. Uses state_change events to track states."""
def __init__(self, bus, api):
"""Initalize the statemachine."""
super().__init__(None)
self._api = api
self.mirror()
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)
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)
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
in get_states(self._api)}
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:
self._states.pop(event.data['entity_id'], None)
else:
@ -268,12 +269,14 @@ class StateMachine(ha.StateMachine):
class JSONEncoder(json.JSONEncoder):
""" JSONEncoder that supports Home Assistant objects. """
# pylint: disable=too-few-public-methods,method-hidden
"""JSONEncoder that supports Home Assistant objects."""
# pylint: disable=too-few-public-methods,method-hidden
def default(self, obj):
""" Converts Home Assistant objects and hands
other objects to the original method. """
"""Convert Home Assistant objects.
Hand other objects to the original method.
"""
if hasattr(obj, 'as_dict'):
return obj.as_dict()
@ -291,7 +294,7 @@ class JSONEncoder(json.JSONEncoder):
def validate_api(api):
""" Makes a call to validate API. """
"""Make a call to validate API."""
try:
req = api(METHOD_GET, URL_API)
@ -309,8 +312,7 @@ def validate_api(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 = {
'host': to_api.host,
'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):
""" Disconnects forwarding events from from_api to to_api. """
"""Disconnect forwarding events from from_api to to_api."""
data = {
'host': to_api.host,
'port': to_api.port
@ -359,7 +361,7 @@ def disconnect_remote_events(from_api, to_api):
def get_event_listeners(api):
""" List of events that is being listened for. """
"""List of events that is being listened for."""
try:
req = api(METHOD_GET, URL_API_EVENTS)
@ -373,8 +375,7 @@ def get_event_listeners(api):
def fire_event(api, event_type, data=None):
""" Fire an event at remote API. """
"""Fire an event at remote API."""
try:
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):
""" Queries given API for state of entity_id. """
"""Query given API for state of entity_id."""
try:
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):
""" Queries given API for all states. """
"""Query given API for all states."""
try:
req = api(METHOD_GET,
URL_API_STATES)
@ -424,7 +423,7 @@ def get_states(api):
def remove_state(api, 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:
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):
"""
Tells API to update state for entity_id.
Returns True if success.
"""
"""Tell API to update state for entity_id.
Return True if success.
"""
attributes = attributes or {}
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):
""" 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)
return cur_state and cur_state.state == state
def get_services(api):
"""
Returns a list of dicts. Each dict has a string "domain" and
a list of strings "services".
"""Return a list of dicts.
Each dict has a string "domain" and a list of strings "services".
"""
try:
req = api(METHOD_GET, URL_API_SERVICES)
@ -495,7 +493,7 @@ def get_services(api):
def call_service(api, domain, service, service_data=None):
""" Calls a service at the remote API. """
"""Call a service at the remote API."""
try:
req = api(METHOD_POST,
URL_API_SERVICES_SERVICE.format(domain, service),

View File

@ -1,9 +1,4 @@
"""
homeassistant.util
~~~~~~~~~~~~~~~~~~
Helper methods for various modules.
"""
"""Helper methods for various modules."""
import collections
from itertools import chain
import threading
@ -25,24 +20,24 @@ RE_SLUGIFY = re.compile(r'[^a-z0-9_]+')
def sanitize_filename(filename):
""" Sanitizes a filename by removing .. / and \\. """
r"""Sanitize a filename by removing .. / and \\."""
return RE_SANITIZE_FILENAME.sub("", filename)
def sanitize_path(path):
""" Sanitizes a path by removing ~ and .. """
"""Sanitize a path by removing ~ and .."""
return RE_SANITIZE_PATH.sub("", path)
def slugify(text):
""" Slugifies a given text. """
"""Slugify a given text."""
text = text.lower().replace(" ", "_")
return RE_SLUGIFY.sub("", text)
def repr_helper(inp):
""" Helps creating a more readable string representation of objects. """
"""Help creating a more readable string representation of objects."""
if isinstance(inp, (dict, MappingProxyType)):
return ", ".join(
repr_helper(key)+"="+repr_helper(item) for key, item
@ -54,7 +49,7 @@ def repr_helper(inp):
def convert(value, to_type, default=None):
""" Converts value to to_type, returns default if fails. """
"""Convert value to to_type, returns default if fails."""
try:
return default if value is None else to_type(value)
except (ValueError, TypeError):
@ -63,8 +58,10 @@ def convert(value, to_type, default=None):
def ensure_unique_string(preferred_string, current_strings):
""" Returns a string that is not present in current_strings.
If preferred string exists will append _2, _3, .. """
"""Return a string that is not present in current_strings.
If preferred string exists will append _2, _3, ..
"""
test_string = preferred_string
current_strings = set(current_strings)
@ -79,7 +76,7 @@ def ensure_unique_string(preferred_string, current_strings):
# Taken from: http://stackoverflow.com/a/11735897
def get_local_ip():
""" Tries to determine the local IP address of the machine. """
"""Try to determine the local IP address of the machine."""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@ -95,7 +92,7 @@ def get_local_ip():
# Taken from http://stackoverflow.com/a/23728630
def get_random_string(length=10):
""" Returns a random string with letters and digits. """
"""Return a random string with letters and digits."""
generator = random.SystemRandom()
source_chars = string.ascii_letters + string.digits
@ -103,34 +100,39 @@ def get_random_string(length=10):
class OrderedEnum(enum.Enum):
""" Taken from Python 3.4.0 docs. """
# pylint: disable=no-init, too-few-public-methods
"""Taken from Python 3.4.0 docs."""
# pylint: disable=no-init, too-few-public-methods
def __ge__(self, other):
"""Return the greater than element."""
if self.__class__ is other.__class__:
return self.value >= other.value
return NotImplemented
def __gt__(self, other):
"""Return the greater element."""
if self.__class__ is other.__class__:
return self.value > other.value
return NotImplemented
def __le__(self, other):
"""Return the lower than element."""
if self.__class__ is other.__class__:
return self.value <= other.value
return NotImplemented
def __lt__(self, other):
"""Return the lower element."""
if self.__class__ is other.__class__:
return self.value < other.value
return NotImplemented
class OrderedSet(collections.MutableSet):
""" Ordered set taken from http://code.activestate.com/recipes/576694/ """
"""Ordered set taken from http://code.activestate.com/recipes/576694/."""
def __init__(self, iterable=None):
"""Initialize the set."""
self.end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.map = {} # key --> [key, prev, next]
@ -138,20 +140,22 @@ class OrderedSet(collections.MutableSet):
self |= iterable
def __len__(self):
"""Return the length of the set."""
return len(self.map)
def __contains__(self, key):
"""Check if key is in set."""
return key in self.map
def add(self, key):
""" Add an element to the end of the set. """
"""Add an element to the end of the set."""
if key not in self.map:
end = self.end
curr = end[1]
curr[2] = end[1] = self.map[key] = [key, curr, end]
def promote(self, key):
""" Promote element to beginning of the set, add if not there. """
"""Promote element to beginning of the set, add if not there."""
if key in self.map:
self.discard(key)
@ -160,13 +164,14 @@ class OrderedSet(collections.MutableSet):
curr[2] = begin[1] = self.map[key] = [key, curr, begin]
def discard(self, key):
""" Discard an element from the set. """
"""Discard an element from the set."""
if key in self.map:
key, prev_item, next_item = self.map.pop(key)
prev_item[2] = next_item
next_item[1] = prev_item
def __iter__(self):
"""Iteration of the set."""
end = self.end
curr = end[2]
while curr is not end:
@ -174,6 +179,7 @@ class OrderedSet(collections.MutableSet):
curr = curr[2]
def __reversed__(self):
"""reverse the ordering."""
end = self.end
curr = end[1]
while curr is not end:
@ -181,8 +187,8 @@ class OrderedSet(collections.MutableSet):
curr = curr[1]
def pop(self, last=True): # pylint: disable=arguments-differ
"""
Pops element of the end of the set.
"""Pop element of the end of the set.
Set last=False to pop from the beginning.
"""
if not self:
@ -192,24 +198,27 @@ class OrderedSet(collections.MutableSet):
return key
def update(self, *args):
""" Add elements from args to the set. """
"""Add elements from args to the set."""
for item in chain(*args):
self.add(item)
def __repr__(self):
"""Return the representation."""
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, list(self))
def __eq__(self, other):
"""Return the comparision."""
if isinstance(other, OrderedSet):
return len(self) == len(other) and list(self) == list(other)
return set(self) == set(other)
class Throttle(object):
"""
A method decorator to add a cooldown to a method to prevent it from being
"""A class for throttling the execution of tasks.
This method decorator adds a cooldown to a method to prevent it from being
called more then 1 time within the timedelta interval `min_time` after it
returned its result.
@ -223,13 +232,15 @@ class Throttle(object):
Adds a datetime attribute `last_call` to the method.
"""
# pylint: disable=too-few-public-methods
# pylint: disable=too-few-public-methods
def __init__(self, min_time, limit_no_throttle=None):
"""Initialize the throttle."""
self.min_time = min_time
self.limit_no_throttle = limit_no_throttle
def __call__(self, method):
"""Caller for the throttle."""
if self.limit_no_throttle is not None:
method = Throttle(self.limit_no_throttle)(method)
@ -248,8 +259,8 @@ class Throttle(object):
@wraps(method)
def wrapper(*args, **kwargs):
"""
Wrapper that allows wrapped to be called only once per min_time.
"""Wrapper that allows wrapped to be called only once per min_time.
If we cannot acquire the lock, it is running so return None.
"""
# pylint: disable=protected-access
@ -288,10 +299,11 @@ class Throttle(object):
class ThreadPool(object):
"""A priority queue-based thread pool."""
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-instance-attributes
def __init__(self, job_handler, worker_count=0, busy_callback=None):
"""
"""Initialize the pool.
job_handler: method to be called from worker thread to handle job
worker_count: number of threads to run that handle jobs
busy_callback: method to be called when queue gets too big.
@ -338,18 +350,18 @@ class ThreadPool(object):
self.busy_warning_limit = self.worker_count * 3
def add_job(self, priority, job):
""" Add a job to the queue. """
"""Add a job to the queue."""
with self._lock:
if not self.running:
raise RuntimeError("ThreadPool not running")
self._work_queue.put(PriorityQueueItem(priority, job))
# check if our queue is getting too big
# Check if our queue is getting too big.
if self._work_queue.qsize() > self.busy_warning_limit \
and self._busy_callback is not None:
# Increase limit we will issue next warning
# Increase limit we will issue next warning.
self.busy_warning_limit *= 2
self._busy_callback(
@ -404,12 +416,14 @@ class ThreadPool(object):
class PriorityQueueItem(object):
""" Holds a priority and a value. Used within PriorityQueue. """
"""Holds a priority and a value. Used within PriorityQueue."""
# pylint: disable=too-few-public-methods
def __init__(self, priority, item):
"""Initialize the queue."""
self.priority = priority
self.item = item
def __lt__(self, other):
"""Return the ordering."""
return self.priority < other.priority

View File

@ -1,10 +1,4 @@
"""
homeassistant.util.dt
~~~~~~~~~~~~~~~~~~~~~
Provides helper methods to handle the time in HA.
"""
"""Provides helper methods to handle the time in HA."""
import datetime as dt
import pytz
@ -16,7 +10,7 @@ UTC = DEFAULT_TIME_ZONE = pytz.utc
def set_default_time_zone(time_zone):
""" Sets a default time zone to be used when none is specified. """
"""Set a default time zone to be used when none is specified."""
global DEFAULT_TIME_ZONE # pylint: disable=global-statement
assert isinstance(time_zone, dt.tzinfo)
@ -25,7 +19,7 @@ def set_default_time_zone(time_zone):
def get_time_zone(time_zone_str):
""" Get time zone from string. Return None if unable to determine. """
"""Get time zone from string. Return None if unable to determine."""
try:
return pytz.timezone(time_zone_str)
except pytz.exceptions.UnknownTimeZoneError:
@ -33,18 +27,20 @@ def get_time_zone(time_zone_str):
def utcnow():
""" Get now in UTC time. """
"""Get now in UTC time."""
return dt.datetime.now(UTC)
def now(time_zone=None):
""" Get now in specified time zone. """
"""Get now in specified time zone."""
return dt.datetime.now(time_zone or DEFAULT_TIME_ZONE)
def as_utc(dattim):
""" Return a datetime as UTC time.
Assumes datetime without tzinfo to be in the DEFAULT_TIME_ZONE. """
"""Return a datetime as UTC time.
Assumes datetime without tzinfo to be in the DEFAULT_TIME_ZONE.
"""
if dattim.tzinfo == UTC:
return dattim
elif dattim.tzinfo is None:
@ -54,7 +50,7 @@ def as_utc(dattim):
def as_local(dattim):
""" Converts a UTC datetime object to local time_zone. """
"""Convert a UTC datetime object to local time zone."""
if dattim.tzinfo == DEFAULT_TIME_ZONE:
return dattim
elif dattim.tzinfo is None:
@ -64,12 +60,12 @@ def as_local(dattim):
def utc_from_timestamp(timestamp):
""" Returns a UTC time from a timestamp. """
"""Return a UTC time from a timestamp."""
return dt.datetime.utcfromtimestamp(timestamp).replace(tzinfo=UTC)
def start_of_local_day(dt_or_d=None):
""" Return local datetime object of start of day from date or datetime. """
"""Return local datetime object of start of day from date or datetime."""
if dt_or_d is None:
dt_or_d = now().date()
elif isinstance(dt_or_d, dt.datetime):
@ -80,12 +76,12 @@ def start_of_local_day(dt_or_d=None):
def datetime_to_local_str(dattim):
""" Converts datetime to specified time_zone and returns a string. """
"""Convert datetime to specified time_zone and returns a string."""
return datetime_to_str(as_local(dattim))
def datetime_to_str(dattim):
""" Converts datetime to a string format.
"""Convert datetime to a string format.
@rtype : str
"""
@ -93,7 +89,7 @@ def datetime_to_str(dattim):
def datetime_to_time_str(dattim):
""" Converts datetime to a string containing only the time.
"""Convert datetime to a string containing only the time.
@rtype : str
"""
@ -101,7 +97,7 @@ def datetime_to_time_str(dattim):
def datetime_to_date_str(dattim):
""" Converts datetime to a string containing only the date.
"""Convert datetime to a string containing only the date.
@rtype : str
"""
@ -109,7 +105,7 @@ def datetime_to_date_str(dattim):
def str_to_datetime(dt_str, dt_format=DATETIME_STR_FORMAT):
""" Converts a string to a UTC datetime object.
"""Convert a string to a UTC datetime object.
@rtype: datetime
"""
@ -121,7 +117,7 @@ def str_to_datetime(dt_str, dt_format=DATETIME_STR_FORMAT):
def date_str_to_date(dt_str):
""" Converts a date string to a date object. """
"""Convert a date string to a date object."""
try:
return dt.datetime.strptime(dt_str, DATE_STR_FORMAT).date()
except ValueError: # If dt_str did not match our format
@ -129,13 +125,14 @@ def date_str_to_date(dt_str):
def strip_microseconds(dattim):
""" Returns a copy of dattime object but with microsecond set to 0. """
"""Return a copy of dattime object but with microsecond set to 0."""
return dattim.replace(microsecond=0)
def parse_time_str(time_str):
""" Parse a time string (00:20:00) into Time object.
Return None if invalid.
"""Parse a time string (00:20:00) into Time object.
Return None if invalid.
"""
parts = str(time_str).split(':')
if len(parts) < 2:

View File

@ -1,8 +1,4 @@
"""
homeassistant.util.package
~~~~~~~~~~~~~~~~~~~~~~~~~~
Helpers to install PyPi packages.
"""
"""Helpers to install PyPi packages."""
import logging
import os
import subprocess
@ -17,8 +13,8 @@ INSTALL_LOCK = threading.Lock()
def install_package(package, upgrade=True, target=None):
"""
Install a package on PyPi. Accepts pip compatible package strings.
"""Install a package on PyPi. Accepts pip compatible package strings.
Return boolean if install successful.
"""
# Not using 'import pip; pip.main([])' because it breaks the logger
@ -41,8 +37,8 @@ def install_package(package, upgrade=True, target=None):
def check_package_exists(package, lib_dir):
"""
Check if a package is installed globally or in lib_dir.
"""Check if a package is installed globally or in lib_dir.
Returns True when the requirement is met.
Returns False when the package is not installed or doesn't meet req.
"""

View File

@ -1,15 +1,11 @@
"""
homeassistant.util.temperature
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Temperature util functions.
"""
"""Temperature util functions."""
def fahrenheit_to_celcius(fahrenheit):
""" Convert a Fahrenheit temperature to Celcius. """
"""Convert a Fahrenheit temperature to Celsius."""
return (fahrenheit - 32.0) / 1.8
def celcius_to_fahrenheit(celcius):
""" Convert a Celcius temperature to Fahrenheit. """
"""Convert a Celsius temperature to Fahrenheit."""
return celcius * 1.8 + 32.0

View File

@ -1,6 +1,4 @@
"""
YAML utility functions.
"""
"""YAML utility functions."""
import logging
import os
from collections import OrderedDict
@ -26,8 +24,7 @@ def load_yaml(fname):
def _include_yaml(loader, node):
"""
Loads another YAML file and embeds it using the !include tag.
"""Load another YAML file and embeds it using the !include tag.
Example:
device_tracker: !include device_tracker.yaml
@ -37,9 +34,7 @@ def _include_yaml(loader, node):
def _ordered_dict(loader, node):
"""
Loads YAML mappings into an ordered dict to preserve key order.
"""
"""Load YAML mappings into an ordered dict to preserve key order."""
loader.flatten_mapping(node)
return OrderedDict(loader.construct_pairs(node))