mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Merge branch 'pep257-utils' into dev
This commit is contained in:
commit
7f1e181c9b
@ -0,0 +1 @@
|
||||
"""Init file for Home Assistant."""
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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))
|
||||
|
@ -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))
|
||||
|
@ -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)]
|
||||
|
@ -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):
|
||||
|
@ -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 = []
|
||||
|
@ -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`.
|
||||
"""
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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. "
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user