Merge pull request #70 from balloob/dev

Update master with latest changes
This commit is contained in:
Paulus Schoutsen 2015-03-21 23:26:00 -07:00
commit ea200dea40
78 changed files with 1648 additions and 658 deletions

View File

@ -12,21 +12,21 @@ omit =
homeassistant/components/*/zwave.py homeassistant/components/*/zwave.py
homeassistant/components/*/tellstick.py homeassistant/components/*/tellstick.py
homeassistant/components/*/vera.py
homeassistant/components/keyboard.py homeassistant/components/keyboard.py
homeassistant/components/switch/wemo.py homeassistant/components/switch/wemo.py
homeassistant/components/thermostat/nest.py homeassistant/components/thermostat/nest.py
homeassistant/components/light/hue.py homeassistant/components/light/hue.py
homeassistant/components/sensor/systemmonitor.py homeassistant/components/sensor/systemmonitor.py
homeassistant/components/sensor/sabnzbd.py
homeassistant/components/notify/pushbullet.py homeassistant/components/notify/pushbullet.py
homeassistant/components/notify/pushover.py
homeassistant/components/media_player/cast.py homeassistant/components/media_player/cast.py
homeassistant/components/device_tracker/luci.py homeassistant/components/device_tracker/luci.py
homeassistant/components/device_tracker/tomato.py homeassistant/components/device_tracker/tomato.py
homeassistant/components/device_tracker/netgear.py homeassistant/components/device_tracker/netgear.py
homeassistant/components/device_tracker/nmap_tracker.py homeassistant/components/device_tracker/nmap_tracker.py
homeassistant/components/light/vera.py
homeassistant/components/sensor/vera.py
homeassistant/components/switch/vera.py
[report] [report]

3
.gitmodules vendored
View File

@ -16,3 +16,6 @@
[submodule "homeassistant/external/vera"] [submodule "homeassistant/external/vera"]
path = homeassistant/external/vera path = homeassistant/external/vera
url = https://github.com/jamespcole/home-assistant-vera-api.git url = https://github.com/jamespcole/home-assistant-vera-api.git
[submodule "homeassistant/external/nzbclients"]
path = homeassistant/external/nzbclients
url = https://github.com/jamespcole/home-assistant-nzb-clients.git

View File

@ -4,7 +4,7 @@ MAINTAINER Paulus Schoutsen <Paulus@PaulusSchoutsen.nl>
VOLUME /config VOLUME /config
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y cython3 libudev-dev python-sphinx python3-setuptools mercurial && \ apt-get install -y cython3 libudev-dev && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
pip3 install cython && \ pip3 install cython && \
scripts/build_python_openzwave scripts/build_python_openzwave

View File

@ -1,8 +1,20 @@
homeassistant: homeassistant:
# Omitted values in this section will be auto detected using freegeoip.net
# Location required to calculate the time the sun rises and sets # Location required to calculate the time the sun rises and sets
latitude: 32.87336 latitude: 32.87336
longitude: 117.22743 longitude: 117.22743
# C for Celcius, F for Fahrenheit
temperature_unit: C
# Pick yours from here:
# http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
time_zone: America/Los_Angeles
# Name of the location where Home Assistant is running
name: Home
http: http:
api_password: mypass api_password: mypass
# Set to 1 to enable development mode # Set to 1 to enable development mode
@ -120,4 +132,31 @@ sensor:
- type: 'memory_free' - type: 'memory_free'
- type: 'processor_use' - type: 'processor_use'
- type: 'process' - type: 'process'
arg: 'octave-cli' arg: 'octave-cli'
script:
# Turns on the bedroom lights and then the living room lights 1 minute later
wakeup:
alias: Wake Up
sequence:
# alias is optional
- alias: Bedroom lights on
execute_service: light.turn_on
service_data:
entity_id: group.bedroom
- delay:
# supports seconds, milliseconds, minutes, hours, etc.
minutes: 1
- alias: Living room lights on
execute_service: light.turn_on
service_data:
entity_id: group.living_room
scene:
- name: Romantic
entities:
light.tv_back_light: on
light.ceiling:
state: on
color: [0.33, 0.66]
brightness: 200

View File

@ -15,11 +15,14 @@ import re
import datetime as dt import datetime as dt
import functools as ft import functools as ft
import requests
from homeassistant.const import ( from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED, SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED,
EVENT_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL, EVENT_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL,
EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED) EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED,
TEMP_CELCIUS, TEMP_FAHRENHEIT)
import homeassistant.util as util import homeassistant.util as util
DOMAIN = "homeassistant" DOMAIN = "homeassistant"
@ -49,19 +52,34 @@ class HomeAssistant(object):
self.bus = EventBus(pool) self.bus = EventBus(pool)
self.services = ServiceRegistry(self.bus, pool) self.services = ServiceRegistry(self.bus, pool)
self.states = StateMachine(self.bus) self.states = StateMachine(self.bus)
self.config = Config()
# List of loaded components @property
self.components = [] def components(self):
""" DEPRECATED 3/21/2015. Use hass.config.components """
_LOGGER.warning(
'hass.components is deprecated. Use hass.config.components')
return self.config.components
# Remote.API object pointing at local API @property
self.local_api = None def local_api(self):
""" DEPRECATED 3/21/2015. Use hass.config.api """
_LOGGER.warning(
'hass.local_api is deprecated. Use hass.config.api')
return self.config.api
# Directory that holds the configuration @property
self.config_dir = os.path.join(os.getcwd(), 'config') def config_dir(self):
""" DEPRECATED 3/18/2015. Use hass.config.config_dir """
_LOGGER.warning(
'hass.config_dir is deprecated. Use hass.config.config_dir')
return self.config.config_dir
def get_config_path(self, path): def get_config_path(self, path):
""" Returns path to the file within the config dir. """ """ DEPRECATED 3/18/2015. Use hass.config.path """
return os.path.join(self.config_dir, path) _LOGGER.warning(
'hass.get_config_path is deprecated. Use hass.config.path')
return self.config.path(path)
def start(self): def start(self):
""" Start home assistant. """ """ Start home assistant. """
@ -115,6 +133,7 @@ class HomeAssistant(object):
action(now) action(now)
self.bus.listen(EVENT_TIME_CHANGED, point_in_time_listener) self.bus.listen(EVENT_TIME_CHANGED, point_in_time_listener)
return point_in_time_listener
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments
def track_time_change(self, action, def track_time_change(self, action,
@ -154,6 +173,7 @@ class HomeAssistant(object):
action(event.data[ATTR_NOW]) action(event.data[ATTR_NOW])
self.bus.listen(EVENT_TIME_CHANGED, time_listener) self.bus.listen(EVENT_TIME_CHANGED, time_listener)
return time_listener
def stop(self): def stop(self):
""" Stops Home Assistant and shuts down all threads. """ """ Stops Home Assistant and shuts down all threads. """
@ -457,6 +477,11 @@ class State(object):
self.last_changed = util.strip_microseconds( self.last_changed = util.strip_microseconds(
last_changed or self.last_updated) last_changed or self.last_updated)
@property
def domain(self):
""" Returns domain of this state. """
return util.split_entity_id(self.entity_id)[0]
def copy(self): def copy(self):
""" Creates a copy of itself. """ """ Creates a copy of itself. """
return State(self.entity_id, self.state, return State(self.entity_id, self.state,
@ -829,6 +854,83 @@ class Timer(threading.Thread):
self.hass.bus.fire(EVENT_TIME_CHANGED, {ATTR_NOW: now}) self.hass.bus.fire(EVENT_TIME_CHANGED, {ATTR_NOW: now})
class Config(object):
""" Configuration settings for Home Assistant. """
# pylint: disable=too-many-instance-attributes
def __init__(self):
self.latitude = None
self.longitude = None
self.temperature_unit = None
self.location_name = None
self.time_zone = None
# List of loaded components
self.components = []
# Remote.API object pointing at local API
self.api = None
# Directory that holds the configuration
self.config_dir = os.path.join(os.getcwd(), 'config')
def auto_detect(self):
""" Will attempt to detect config of Home Assistant. """
# Only detect if location or temp unit missing
if None not in (self.latitude, self.longitude, self.temperature_unit):
return
_LOGGER.info('Auto detecting location and temperature unit')
try:
info = requests.get('https://freegeoip.net/json/').json()
except requests.RequestException:
return
if self.latitude is None and self.longitude is None:
self.latitude = info['latitude']
self.longitude = info['longitude']
if self.temperature_unit is None:
# From Wikipedia:
# Fahrenheit is used in the Bahamas, Belize, the Cayman Islands,
# Palau, and the United States and associated territories of
# American Samoa and the U.S. Virgin Islands
if info['country_code'] in ('BS', 'BZ', 'KY', 'PW',
'US', 'AS', 'VI'):
self.temperature_unit = TEMP_FAHRENHEIT
else:
self.temperature_unit = TEMP_CELCIUS
if self.location_name is None:
self.location_name = info['city']
if self.time_zone is None:
self.time_zone = info['time_zone']
def path(self, path):
""" Returns path to the file within the config dir. """
return os.path.join(self.config_dir, path)
def temperature(self, value, unit):
""" Converts temperature to user preferred unit if set. """
if not (unit and self.temperature_unit and
unit != self.temperature_unit):
return value, unit
try:
if unit == TEMP_CELCIUS:
# Convert C to F
return round(float(value) * 1.8 + 32.0, 1), TEMP_FAHRENHEIT
# Convert F to C
return round((float(value)-32.0)/1.8, 1), TEMP_CELCIUS
except ValueError:
# Could not convert value to float
return value, unit
class HomeAssistantError(Exception): class HomeAssistantError(Exception):
""" General Home Assistant exception occured. """ """ General Home Assistant exception occured. """
pass pass

View File

@ -20,7 +20,10 @@ import homeassistant
import homeassistant.loader as loader import homeassistant.loader as loader
import homeassistant.components as core_components import homeassistant.components as core_components
import homeassistant.components.group as group import homeassistant.components.group as group
from homeassistant.const import EVENT_COMPONENT_LOADED from homeassistant.const import (
EVENT_COMPONENT_LOADED, CONF_LATITUDE, CONF_LONGITUDE,
CONF_TEMPERATURE_UNIT, CONF_NAME, CONF_TIME_ZONE, TEMP_CELCIUS,
TEMP_FAHRENHEIT)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -28,21 +31,49 @@ ATTR_COMPONENT = "component"
def setup_component(hass, domain, config=None): def setup_component(hass, domain, config=None):
""" Setup a component for Home Assistant. """ """ Setup a component and all its dependencies. """
# Check if already loaded
if domain in hass.components: if domain in hass.config.components:
return return True
_ensure_loader_prepared(hass) _ensure_loader_prepared(hass)
if config is None: if config is None:
config = defaultdict(dict) config = defaultdict(dict)
components = loader.load_order_component(domain)
# OrderedSet is empty if component or dependencies could not be resolved
if not components:
return False
for component in components:
if component in hass.config.components:
continue
if not _setup_component(hass, component, config):
return False
return True
def _setup_component(hass, domain, config):
""" Setup a component for Home Assistant. """
component = loader.get_component(domain) component = loader.get_component(domain)
missing_deps = [dep for dep in component.DEPENDENCIES
if dep not in hass.config.components]
if missing_deps:
_LOGGER.error(
"Not initializing %s because not all dependencies loaded: %s",
domain, ", ".join(missing_deps))
return False
try: try:
if component.setup(hass, config): if component.setup(hass, config):
hass.components.append(component.DOMAIN) hass.config.components.append(component.DOMAIN)
# Assumption: if a component does not depend on groups # Assumption: if a component does not depend on groups
# it communicates with devices # it communicates with devices
@ -73,6 +104,8 @@ def from_config_dict(config, hass=None):
if hass is None: if hass is None:
hass = homeassistant.HomeAssistant() hass = homeassistant.HomeAssistant()
process_ha_core_config(hass, config.get(homeassistant.DOMAIN, {}))
enable_logging(hass) enable_logging(hass)
_ensure_loader_prepared(hass) _ensure_loader_prepared(hass)
@ -97,7 +130,7 @@ def from_config_dict(config, hass=None):
# Setup the components # Setup the components
for domain in loader.load_order_components(components): for domain in loader.load_order_components(components):
setup_component(hass, domain, config) _setup_component(hass, domain, config)
return hass return hass
@ -111,14 +144,19 @@ def from_config_file(config_path, hass=None):
if hass is None: if hass is None:
hass = homeassistant.HomeAssistant() hass = homeassistant.HomeAssistant()
# Set config dir to directory holding config file # Set config dir to directory holding config file
hass.config_dir = os.path.abspath(os.path.dirname(config_path)) hass.config.config_dir = os.path.abspath(os.path.dirname(config_path))
config_dict = {} config_dict = {}
# check config file type # check config file type
if os.path.splitext(config_path)[1] == '.yaml': if os.path.splitext(config_path)[1] == '.yaml':
# Read yaml # Read yaml
config_dict = yaml.load(io.open(config_path, 'r')) config_dict = yaml.load(io.open(config_path, 'r'))
# If YAML file was empty
if config_dict is None:
config_dict = {}
else: else:
# Read config # Read config
config = configparser.ConfigParser() config = configparser.ConfigParser()
@ -138,13 +176,13 @@ def enable_logging(hass):
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
# Log errors to a file if we have write access to file or config dir # Log errors to a file if we have write access to file or config dir
err_log_path = hass.get_config_path("home-assistant.log") err_log_path = hass.config.path("home-assistant.log")
err_path_exists = os.path.isfile(err_log_path) err_path_exists = os.path.isfile(err_log_path)
# Check if we can write to the error log if it exists or that # Check if we can write to the error log if it exists or that
# we can create files in the containing directory if not. # we can create files in the containing directory if not.
if (err_path_exists and os.access(err_log_path, os.W_OK)) or \ if (err_path_exists and os.access(err_log_path, os.W_OK)) or \
(not err_path_exists and os.access(hass.config_dir, os.W_OK)): (not err_path_exists and os.access(hass.config.config_dir, os.W_OK)):
err_handler = logging.FileHandler( err_handler = logging.FileHandler(
err_log_path, mode='w', delay=True) err_log_path, mode='w', delay=True)
@ -160,6 +198,26 @@ def enable_logging(hass):
"Unable to setup error log %s (access denied)", err_log_path) "Unable to setup error log %s (access denied)", err_log_path)
def process_ha_core_config(hass, config):
""" Processes the [homeassistant] section from the config. """
for key, attr in ((CONF_LATITUDE, 'latitude'),
(CONF_LONGITUDE, 'longitude'),
(CONF_NAME, 'location_name'),
(CONF_TIME_ZONE, 'time_zone')):
if key in config:
setattr(hass.config, attr, config[key])
if CONF_TEMPERATURE_UNIT in config:
unit = config[CONF_TEMPERATURE_UNIT]
if unit == 'C':
hass.config.temperature_unit = TEMP_CELCIUS
elif unit == 'F':
hass.config.temperature_unit = TEMP_FAHRENHEIT
hass.config.auto_detect()
def _ensure_loader_prepared(hass): def _ensure_loader_prepared(hass):
""" Ensure Home Assistant loader is prepared. """ """ Ensure Home Assistant loader is prepared. """
if not loader.PREPARED: if not loader.PREPARED:

View File

@ -10,7 +10,7 @@ import threading
import json import json
import homeassistant as ha import homeassistant as ha
from homeassistant.helpers import TrackStates from homeassistant.helpers.state import TrackStates
import homeassistant.remote as rem import homeassistant.remote as rem
from homeassistant.const import ( from homeassistant.const import (
URL_API, URL_API_STATES, URL_API_EVENTS, URL_API_SERVICES, URL_API_STREAM, URL_API, URL_API_STATES, URL_API_EVENTS, URL_API_SERVICES, URL_API_STREAM,
@ -32,7 +32,7 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config): def setup(hass, config):
""" Register the API with the HTTP interface. """ """ Register the API with the HTTP interface. """
if 'http' not in hass.components: if 'http' not in hass.config.components:
_LOGGER.error('Dependency http is not loaded') _LOGGER.error('Dependency http is not loaded')
return False return False
@ -311,4 +311,4 @@ def _handle_delete_api_event_forward(handler, path_match, data):
def _handle_get_api_components(handler, path_match, data): def _handle_get_api_components(handler, path_match, data):
""" Returns all the loaded components. """ """ Returns all the loaded components. """
handler.write_json(handler.server.hass.components) handler.write_json(handler.server.hass.config.components)

View File

@ -83,8 +83,8 @@ def _get_instance(hass):
except KeyError: except KeyError:
_INSTANCES[hass] = Configurator(hass) _INSTANCES[hass] = Configurator(hass)
if DOMAIN not in hass.components: if DOMAIN not in hass.config.components:
hass.components.append(DOMAIN) hass.config.components.append(DOMAIN)
return _INSTANCES[hass] return _INSTANCES[hass]

View File

@ -10,15 +10,14 @@ import homeassistant as ha
import homeassistant.bootstrap as bootstrap import homeassistant.bootstrap as bootstrap
import homeassistant.loader as loader import homeassistant.loader as loader
from homeassistant.const import ( from homeassistant.const import (
CONF_PLATFORM, ATTR_ENTITY_PICTURE, CONF_PLATFORM, ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID)
CONF_LATITUDE, CONF_LONGITUDE)
DOMAIN = "demo" DOMAIN = "demo"
DEPENDENCIES = [] DEPENDENCIES = []
COMPONENTS_WITH_DEMO_PLATFORM = [ COMPONENTS_WITH_DEMO_PLATFORM = [
'switch', 'light', 'thermostat', 'sensor', 'media_player'] 'switch', 'light', 'thermostat', 'sensor', 'media_player', 'notify']
def setup(hass, config): def setup(hass, config):
@ -29,16 +28,10 @@ def setup(hass, config):
config.setdefault(ha.DOMAIN, {}) config.setdefault(ha.DOMAIN, {})
config.setdefault(DOMAIN, {}) config.setdefault(DOMAIN, {})
if config[DOMAIN].get('hide_demo_state') != '1': if config[DOMAIN].get('hide_demo_state') != 1:
hass.states.set('a.Demo_Mode', 'Enabled') hass.states.set('a.Demo_Mode', 'Enabled')
# Setup sun # Setup sun
if CONF_LATITUDE not in config[ha.DOMAIN]:
config[ha.DOMAIN][CONF_LATITUDE] = '32.87336'
if CONF_LONGITUDE not in config[ha.DOMAIN]:
config[ha.DOMAIN][CONF_LONGITUDE] = '-117.22743'
loader.get_component('sun').setup(hass, config) loader.get_component('sun').setup(hass, config)
# Setup demo platforms # Setup demo platforms
@ -52,18 +45,57 @@ def setup(hass, config):
group.setup_group(hass, 'living room', [lights[0], lights[1], switches[0]]) group.setup_group(hass, 'living room', [lights[0], lights[1], switches[0]])
group.setup_group(hass, 'bedroom', [lights[2], switches[1]]) group.setup_group(hass, 'bedroom', [lights[2], switches[1]])
# Setup device tracker # Setup scripts
hass.states.set("device_tracker.Paulus", "home", bootstrap.setup_component(
hass, 'script',
{'script': {
'demo': {
'alias': 'Demo {}'.format(lights[0]),
'sequence': [{
'execute_service': 'light.turn_off',
'service_data': {ATTR_ENTITY_ID: lights[0]}
}, {
'delay': {'seconds': 5}
}, {
'execute_service': 'light.turn_on',
'service_data': {ATTR_ENTITY_ID: lights[0]}
}, {
'delay': {'seconds': 5}
}, {
'execute_service': 'light.turn_off',
'service_data': {ATTR_ENTITY_ID: lights[0]}
}]
}}})
# Setup scenes
bootstrap.setup_component(
hass, 'scene',
{'scene': [
{'name': 'Romantic lights',
'entities': {
lights[0]: True,
lights[1]: {'state': 'on', 'xy_color': [0.33, 0.66],
'brightness': 200},
}},
{'name': 'Switch on and off',
'entities': {
switches[0]: True,
switches[1]: False,
}},
]})
# Setup fake device tracker
hass.states.set("device_tracker.paulus", "home",
{ATTR_ENTITY_PICTURE: {ATTR_ENTITY_PICTURE:
"http://graph.facebook.com/schoutsen/picture"}) "http://graph.facebook.com/schoutsen/picture"})
hass.states.set("device_tracker.Anne_Therese", "not_home", hass.states.set("device_tracker.anne_therese", "not_home",
{ATTR_ENTITY_PICTURE: {ATTR_ENTITY_PICTURE:
"http://graph.facebook.com/anne.t.frederiksen/picture"}) "http://graph.facebook.com/anne.t.frederiksen/picture"})
hass.states.set("group.all_devices", "home", hass.states.set("group.all_devices", "home",
{ {
"auto": True, "auto": True,
"entity_id": [ ATTR_ENTITY_ID: [
"device_tracker.Paulus", "device_tracker.Paulus",
"device_tracker.Anne_Therese" "device_tracker.Anne_Therese"
] ]

View File

@ -153,7 +153,7 @@ def setup(hass, config):
logger.info( logger.info(
"Everyone has left but there are lights on. Turning them off") "Everyone has left but there are lights on. Turning them off")
light.turn_off(hass) light.turn_off(hass, light_ids)
# Track home coming of each device # Track home coming of each device
hass.states.track_change( hass.states.track_change(

View File

@ -179,7 +179,7 @@ class DeviceTracker(object):
# Write new devices to known devices file # Write new devices to known devices file
if not self.invalid_known_devices_file: if not self.invalid_known_devices_file:
known_dev_path = self.hass.get_config_path(KNOWN_DEVICES_FILE) known_dev_path = self.hass.config.path(KNOWN_DEVICES_FILE)
try: try:
# If file does not exist we will write the header too # If file does not exist we will write the header too
@ -214,7 +214,7 @@ class DeviceTracker(object):
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
def _read_known_devices_file(self): def _read_known_devices_file(self):
""" Parse and process the known devices file. """ """ Parse and process the known devices file. """
known_dev_path = self.hass.get_config_path(KNOWN_DEVICES_FILE) known_dev_path = self.hass.config.path(KNOWN_DEVICES_FILE)
# Return if no known devices file exists # Return if no known devices file exists
if not os.path.isfile(known_dev_path): if not os.path.isfile(known_dev_path):
@ -236,7 +236,7 @@ class DeviceTracker(object):
try: try:
for row in csv.DictReader(inp): for row in csv.DictReader(inp):
device = row['device'] device = row['device'].upper()
if row['track'] == '1': if row['track'] == '1':
if device in self.tracked: if device in self.tracked:

View File

@ -70,12 +70,17 @@ def setup(hass, config):
def new_service_listener(service, info): def new_service_listener(service, info):
""" Called when a new service is found. """ """ Called when a new service is found. """
with lock: with lock:
component = SERVICE_HANDLERS.get(service)
logger.info("Found new service: %s %s", service, info) logger.info("Found new service: %s %s", service, info)
if component and component not in hass.components: component = SERVICE_HANDLERS.get(service)
bootstrap.setup_component(hass, component, config)
# We do not know how to handle this service
if not component:
return
# This component cannot be setup.
if not bootstrap.setup_component(hass, component, config):
return
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, { hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
ATTR_SERVICE: service, ATTR_SERVICE: service,

View File

@ -22,7 +22,7 @@ _LOGGER = logging.getLogger(__name__)
def setup(hass, config): def setup(hass, config):
""" Setup serving the frontend. """ """ Setup serving the frontend. """
if 'http' not in hass.components: if 'http' not in hass.config.components:
_LOGGER.error('Dependency http is not loaded') _LOGGER.error('Dependency http is not loaded')
return False return False

View File

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """ """ DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "08fb2ffccc72d7bfa0ad3478f2e8cfe7" VERSION = "a063d1482fd49e9297d64e1329324f1c"

File diff suppressed because one or more lines are too long

View File

@ -1,23 +1,11 @@
<link rel="import" href="../bower_components/polymer/polymer.html"> <link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="./state-card-display.html">
<link rel="import" href="../components/state-info.html"> <link rel="import" href="../components/state-info.html">
<polymer-element name="state-card-configurator" attributes="stateObj" noscript> <polymer-element name="state-card-configurator" attributes="stateObj" noscript>
<template> <template>
<style> <state-card-display stateObj="{{stateObj}}"></state-card-display>
.state {
margin-left: 16px;
text-transform: capitalize;
font-weight: 300;
font-size: 1.3rem;
text-align: right;
}
</style>
<div horizontal justified layout>
<state-info stateObj="{{stateObj}}"></state-info>
<div class='state'>{{stateObj.stateDisplay}}</div>
</div>
<!-- pre load the image so the dialog is rendered the proper size --> <!-- pre load the image so the dialog is rendered the proper size -->
<template if="{{stateObj.attributes.description_image}}"> <template if="{{stateObj.attributes.description_image}}">

View File

@ -4,6 +4,7 @@
<link rel="import" href="state-card-toggle.html"> <link rel="import" href="state-card-toggle.html">
<link rel="import" href="state-card-thermostat.html"> <link rel="import" href="state-card-thermostat.html">
<link rel="import" href="state-card-configurator.html"> <link rel="import" href="state-card-configurator.html">
<link rel="import" href="state-card-scene.html">
<polymer-element name="state-card-content" attributes="stateObj"> <polymer-element name="state-card-content" attributes="stateObj">
<template> <template>

View File

@ -0,0 +1,27 @@
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="./state-card-display.html">
<link rel="import" href="./state-card-toggle.html">
<polymer-element name="state-card-scene" attributes="stateObj">
<template>
<template if={{allowToggle}}>
<state-card-toggle stateObj="{{stateObj}}"></state-card-toggle>
</template>
<template if={{!allowToggle}}>
<state-card-display stateObj="{{stateObj}}"></state-card-display>
</template>
</template>
<script>
(function() {
Polymer({
allowToggle: false,
stateObjChanged: function(oldVal, newVal) {
this.allowToggle = newVal.state === 'off' ||
newVal.attributes.active_requested;
},
});
})();
</script>
</polymer-element>

View File

@ -48,7 +48,7 @@
}, },
eventStoreChanged: function(eventStore) { eventStoreChanged: function(eventStore) {
this.events = eventStore.all; this.events = eventStore.all.toArray();
}, },
handleClick: function(ev) { handleClick: function(ev) {

View File

@ -24,9 +24,11 @@
var toast = this.$.toast; var toast = this.$.toast;
var notification = notificationStore.lastNotification; var notification = notificationStore.lastNotification;
this.lastId = notification.id; if (notification) {
toast.text = notification.message; this.lastId = notification.id;
toast.show(); toast.text = notification.message;
toast.show();
}
} }
}, },

View File

@ -37,9 +37,7 @@
<template repeat="{{domain in domains}}"> <template repeat="{{domain in domains}}">
<core-submenu icon="{{domain | getIcon}}" label="{{domain}}"> <core-submenu icon="{{domain | getIcon}}" label="{{domain}}">
<template repeat="{{service in domain | getServices}}"> <template repeat="{{service in domain | getServices}}">
<a on-click={{serviceClicked}} data-domain={{domain}}> <a on-click={{serviceClicked}} data-domain={{domain}}>{{service}}</a>
{{service}}
</a>
</template> </template>
</core-submenu> </core-submenu>
</template> </template>

@ -1 +1 @@
Subproject commit 642a83e437fed356db3e13d5a5b0c28d4b3fb713 Subproject commit e048bf6ece91983b9f03aafeb414ae5c535288a2

View File

@ -23,24 +23,20 @@
<core-style ref="ha-headers"></core-style> <core-style ref="ha-headers"></core-style>
<style> <style>
core-header-panel { .sidenav {
background: #fafafa; background: #fafafa;
box-shadow: 1px 0 1px rgba(0, 0, 0, 0.1); box-shadow: 1px 0 1px rgba(0, 0, 0, 0.1);
color: #757575; color: #757575;
overflow: hidden; overflow: hidden;
} }
core-menu core-icon {
margin-right: 24px;
}
core-toolbar { core-toolbar {
font-weight: normal; font-weight: normal;
padding-left: 24px; padding-left: 24px;
} }
core-menu { .sidenav-menu {
overflow: scroll; overflow: auto;
position: absolute; position: absolute;
top: 0px; top: 0px;
left: 0px; left: 0px;
@ -48,7 +44,11 @@
bottom: 0px; bottom: 0px;
} }
paper-item { .sidenav-menu core-icon {
margin-right: 24px;
}
.sidenav-menu > paper-item {
min-height: 53px; min-height: 53px;
} }
@ -70,21 +70,24 @@
<ha-modals></ha-modals> <ha-modals></ha-modals>
<core-drawer-panel id="drawer" on-core-responsive-change="{{responsiveChanged}}"> <core-drawer-panel id="drawer" on-core-responsive-change="{{responsiveChanged}}">
<core-header-panel mode="scroll" drawer> <core-header-panel mode="scroll" drawer class='sidenav'>
<core-toolbar> <core-toolbar>
Home Assistant Home Assistant
</core-toolbar> </core-toolbar>
<core-menu id="menu" <core-menu id="menu" class="sidenav-menu"
selected="0" excludedLocalNames="div" on-core-select="{{menuSelect}}" selected="0" excludedLocalNames="div" on-core-select="{{menuSelect}}"
layout vertical> layout vertical>
<paper-item data-panel="states"> <paper-item data-panel="states">
<core-icon icon="apps"></core-icon> <core-icon icon="apps"></core-icon>
States States
</paper-item> </paper-item>
<paper-item data-panel="group">
<core-icon icon="homeassistant-24:group"></core-icon> <template repeat="{{activeFilters as filter}}">
Groups <paper-item data-panel="states_{{filter}}">
</paper-item> <core-icon icon="{{filter | filterIcon}}"></core-icon>
{{filter | filterName}}
</paper-item>
</template>
<template if="{{hasHistoryComponent}}"> <template if="{{hasHistoryComponent}}">
<paper-item data-panel="history"> <paper-item data-panel="history">
@ -124,10 +127,10 @@
This is the main partial, never remove it from the DOM but hide it This is the main partial, never remove it from the DOM but hide it
to speed up when people click on states. to speed up when people click on states.
--> -->
<partial-states hidden?="{{selected != 'states' && selected != 'group'}}" <partial-states hidden?="{{hideStates}}"
main narrow="{{narrow}}" main narrow="{{narrow}}"
togglePanel="{{togglePanel}}" togglePanel="{{togglePanel}}"
filter="{{selected == 'group' ? 'group' : null}}"> filter="{{stateFilter}}">
</partial-states> </partial-states>
<template if="{{selected == 'history'}}"> <template if="{{selected == 'history'}}">
@ -146,17 +149,24 @@
</template> </template>
<script> <script>
(function() {
var storeListenerMixIn = window.hass.storeListenerMixIn; var storeListenerMixIn = window.hass.storeListenerMixIn;
var authActions = window.hass.authActions; var authActions = window.hass.authActions;
var uiUtil = window.hass.uiUtil;
var uiConstants = window.hass.uiConstants;
Polymer(Polymer.mixin({ Polymer(Polymer.mixin({
selected: "states", selected: "states",
stateFilter: null,
narrow: false, narrow: false,
activeFilters: [],
hasHistoryComponent: false, hasHistoryComponent: false,
isStreaming: false, isStreaming: false,
hasStreamError: false, hasStreamError: false,
hideStates: false,
attached: function() { attached: function() {
this.togglePanel = this.togglePanel.bind(this); this.togglePanel = this.togglePanel.bind(this);
@ -167,8 +177,15 @@ Polymer(Polymer.mixin({
this.stopListeningToStores(); this.stopListeningToStores();
}, },
stateStoreChanged: function(stateStore) {
this.activeFilters = stateStore.domains.filter(function(domain) {
return domain in uiConstants.STATE_FILTERS;
}).toArray();
},
componentStoreChanged: function(componentStore) { componentStoreChanged: function(componentStore) {
this.hasHistoryComponent = componentStore.isLoaded('history'); this.hasHistoryComponent = componentStore.isLoaded('history');
this.hasScriptComponent = componentStore.isLoaded('script');
}, },
streamStoreChanged: function(streamStore) { streamStoreChanged: function(streamStore) {
@ -194,6 +211,14 @@ Polymer(Polymer.mixin({
this.togglePanel(); this.togglePanel();
this.selected = newChoice; this.selected = newChoice;
} }
if (this.selected.substr(0, 7) === 'states_') {
this.hideStates = false;
this.stateFilter = this.selected.substr(7);
} else {
this.hideStates = this.selected !== 'states';
this.stateFilter = null;
}
}, },
responsiveChanged: function(ev, detail, sender) { responsiveChanged: function(ev, detail, sender) {
@ -207,6 +232,15 @@ Polymer(Polymer.mixin({
handleLogOutClick: function() { handleLogOutClick: function() {
authActions.logOut(); authActions.logOut();
}, },
filterIcon: function(filter) {
return uiUtil.domainIcon(filter);
},
filterName: function(filter) {
return uiConstants.STATE_FILTERS[filter];
},
}, storeListenerMixIn)); }, storeListenerMixIn));
})();
</script> </script>
</polymer-element> </polymer-element>

View File

@ -45,7 +45,7 @@
<div class='sidebar'> <div class='sidebar'>
<b>Available services:</b> <b>Available services:</b>
<services-list cbServiceClicked={{serviceSelected}}></event-list> <services-list cbServiceClicked={{serviceSelected}}></services-list>
</div> </div>
</div> </div>
</div> </div>

View File

@ -64,12 +64,12 @@
</partial-base> </partial-base>
</template> </template>
<script> <script>
(function(){
var storeListenerMixIn = window.hass.storeListenerMixIn; var storeListenerMixIn = window.hass.storeListenerMixIn;
var syncActions = window.hass.syncActions; var syncActions = window.hass.syncActions;
var voiceActions = window.hass.voiceActions; var voiceActions = window.hass.voiceActions;
var stateStore = window.hass.stateStore; var stateStore = window.hass.stateStore;
var uiConstants = window.hass.uiConstants;
var stateGroupFilter = function(state) { return state.domain === 'group'; };
Polymer(Polymer.mixin({ Polymer(Polymer.mixin({
headerTitle: "States", headerTitle: "States",
@ -119,30 +119,29 @@
this.isTransmitting = voiceStore.isTransmitting; this.isTransmitting = voiceStore.isTransmitting;
this.finalTranscript = voiceStore.finalTranscript; this.finalTranscript = voiceStore.finalTranscript;
this.interimTranscript = voiceStore.interimTranscript.slice( this.interimTranscript = voiceStore.interimTranscript.slice(
this.finalTranscript.length) this.finalTranscript.length);
}, },
filterChanged: function() { filterChanged: function() {
this.refreshStates(); this.refreshStates();
switch (this.filter) { this.headerTitle = uiConstants.STATE_FILTERS[this.filter] || 'States';
case "group":
this.headerTitle = "Groups";
break;
default:
this.headerTitle = "States";
break;
}
}, },
refreshStates: function() { refreshStates: function() {
var states = stateStore.all; var states;
if (this.filter) {
var filter = this.filter;
states = stateStore.all.filter(function(state) {
return state.domain === filter;
});
if (this.filter === 'group') {
states = states.filter(stateGroupFilter);
} else { } else {
states = states.filterNot(stateGroupFilter); // all but the STATE_FILTER keys
states = stateStore.all.filter(function(state) {
return !(state.domain in uiConstants.STATE_FILTERS);
});
} }
this.states = states.toArray(); this.states = states.toArray();
@ -160,5 +159,6 @@
} }
}, },
}, storeListenerMixIn)); }, storeListenerMixIn));
})();
</script> </script>
</polymer> </polymer>

View File

@ -6,6 +6,7 @@
<link rel="import" href="more-info-sun.html"> <link rel="import" href="more-info-sun.html">
<link rel="import" href="more-info-configurator.html"> <link rel="import" href="more-info-configurator.html">
<link rel="import" href="more-info-thermostat.html"> <link rel="import" href="more-info-thermostat.html">
<link rel="import" href="more-info-script.html">
<polymer-element name="more-info-content" attributes="stateObj"> <polymer-element name="more-info-content" attributes="stateObj">
<template> <template>

View File

@ -0,0 +1,22 @@
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/core-style/core-style.html">
<polymer-element name="more-info-script" attributes="stateObj" noscript>
<template>
<core-style ref='ha-key-value-table'></core-style>
<style>
.data-entry .value {
max-width: 200px;
}
</style>
<div layout vertical>
<div layout justified horizontal class='data-entry'>
<div class='key'>Last Action</div>
<div class='value'>
{{stateObj.attributes.last_action}}
</div>
</div>
</div>
</template>
</polymer-element>

View File

@ -81,6 +81,12 @@ window.hass.uiUtil.domainIcon = function(domain, state) {
case "conversation": case "conversation":
return "av:hearing"; return "av:hearing";
case "script":
return "description";
case 'scene':
return 'social:pages';
default: default:
return "bookmark-outline"; return "bookmark-outline";
} }

View File

@ -2,9 +2,9 @@
<script> <script>
(function() { (function() {
var DOMAINS_WITH_CARD = ['thermostat', 'configurator']; var DOMAINS_WITH_CARD = ['thermostat', 'configurator', 'scene'];
var DOMAINS_WITH_MORE_INFO = [ var DOMAINS_WITH_MORE_INFO = [
'light', 'group', 'sun', 'configurator', 'thermostat' 'light', 'group', 'sun', 'configurator', 'thermostat', 'script'
]; ];
// Register some polymer filters // Register some polymer filters
@ -51,9 +51,17 @@
preferenceStore = window.hass.preferenceStore, preferenceStore = window.hass.preferenceStore,
authActions = window.hass.authActions; authActions = window.hass.authActions;
window.hass.uiActions = { window.hass.uiConstants = {
ACTION_SHOW_DIALOG_MORE_INFO: 'ACTION_SHOW_DIALOG_MORE_INFO', ACTION_SHOW_DIALOG_MORE_INFO: 'ACTION_SHOW_DIALOG_MORE_INFO',
STATE_FILTERS: {
'group': 'Groups',
'script': 'Scripts',
'scene': 'Scenes',
},
};
window.hass.uiActions = {
showMoreInfoDialog: function(entityId) { showMoreInfoDialog: function(entityId) {
dispatcher.dispatch({ dispatcher.dispatch({
actionType: this.ACTION_SHOW_DIALOG_MORE_INFO, actionType: this.ACTION_SHOW_DIALOG_MORE_INFO,
@ -70,6 +78,6 @@
}; };
// UI specific util methods // UI specific util methods
window.hass.uiUtil = {} window.hass.uiUtil = {};
})(); })();
</script> </script>

View File

@ -135,7 +135,7 @@ def setup(hass, config=None):
threading.Thread(target=server.start, daemon=True).start()) threading.Thread(target=server.start, daemon=True).start())
hass.http = server hass.http = server
hass.local_api = rem.API(util.get_local_ip(), api_password, server_port) hass.config.api = rem.API(util.get_local_ip(), api_password, server_port)
return True return True

View File

@ -52,7 +52,7 @@ import logging
import os import os
import csv import csv
from homeassistant.helpers.device_component import DeviceComponent from homeassistant.helpers.entity_component import EntityComponent
import homeassistant.util as util import homeassistant.util as util
from homeassistant.const import ( from homeassistant.const import (
@ -140,7 +140,7 @@ def turn_off(hass, entity_id=None, transition=None):
def setup(hass, config): def setup(hass, config):
""" Exposes light control via statemachine and services. """ """ Exposes light control via statemachine and services. """
component = DeviceComponent( component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS, _LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_LIGHTS) GROUP_NAME_ALL_LIGHTS)
component.setup(config) component.setup(config)
@ -148,7 +148,7 @@ def setup(hass, config):
# Load built-in profiles and custom profiles # Load built-in profiles and custom profiles
profile_paths = [os.path.join(os.path.dirname(__file__), profile_paths = [os.path.join(os.path.dirname(__file__),
LIGHT_PROFILES_FILE), LIGHT_PROFILES_FILE),
hass.get_config_path(LIGHT_PROFILES_FILE)] hass.config.path(LIGHT_PROFILES_FILE)]
profiles = {} profiles = {}
for profile_path in profile_paths: for profile_path in profile_paths:

View File

@ -1,7 +1,7 @@
""" Provides demo lights. """ """ Provides demo lights. """
import random import random
from homeassistant.helpers.device import ToggleDevice from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME
from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_XY_COLOR from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_XY_COLOR
@ -22,7 +22,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
]) ])
class DemoLight(ToggleDevice): class DemoLight(ToggleEntity):
""" Provides a demo switch. """ """ Provides a demo switch. """
def __init__(self, name, state, xy=None, brightness=180): def __init__(self, name, state, xy=None, brightness=180):
self._name = name or DEVICE_DEFAULT_NAME self._name = name or DEVICE_DEFAULT_NAME
@ -30,6 +30,11 @@ class DemoLight(ToggleDevice):
self._xy = xy or random.choice(LIGHT_COLORS) self._xy = xy or random.choice(LIGHT_COLORS)
self._brightness = brightness self._brightness = brightness
@property
def should_poll(self):
""" No polling needed for a demo light. """
return False
@property @property
def name(self): def name(self):
""" Returns the name of the device if any. """ """ Returns the name of the device if any. """

View File

@ -6,7 +6,7 @@ from urllib.parse import urlparse
from homeassistant.loader import get_component from homeassistant.loader import get_component
import homeassistant.util as util import homeassistant.util as util
from homeassistant.helpers.device import ToggleDevice from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION,
@ -51,7 +51,8 @@ def setup_bridge(host, hass, add_devices_callback):
try: try:
bridge = phue.Bridge( bridge = phue.Bridge(
host, config_file_path=hass.get_config_path(PHUE_CONFIG_FILE)) host,
config_file_path=hass.config.path(PHUE_CONFIG_FILE))
except ConnectionRefusedError: # Wrong host was given except ConnectionRefusedError: # Wrong host was given
_LOGGER.exception("Error connecting to the Hue bridge at %s", host) _LOGGER.exception("Error connecting to the Hue bridge at %s", host)
@ -130,7 +131,7 @@ def request_configuration(host, hass, add_devices_callback):
) )
class HueLight(ToggleDevice): class HueLight(ToggleEntity):
""" Represents a Hue light """ """ Represents a Hue light """
def __init__(self, light_id, info, bridge, update_lights): def __init__(self, light_id, info, bridge, update_lights):

View File

@ -3,7 +3,7 @@ import logging
# pylint: disable=no-name-in-module, import-error # pylint: disable=no-name-in-module, import-error
from homeassistant.components.light import ATTR_BRIGHTNESS from homeassistant.components.light import ATTR_BRIGHTNESS
from homeassistant.const import ATTR_FRIENDLY_NAME from homeassistant.const import ATTR_FRIENDLY_NAME
from homeassistant.helpers.device import ToggleDevice from homeassistant.helpers.entity import ToggleEntity
import tellcore.constants as tellcore_constants import tellcore.constants as tellcore_constants
@ -27,7 +27,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
add_devices_callback(lights) add_devices_callback(lights)
class TellstickLight(ToggleDevice): class TellstickLight(ToggleEntity):
""" Represents a tellstick light """ """ Represents a tellstick light """
last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON | last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON |
tellcore_constants.TELLSTICK_TURNOFF | tellcore_constants.TELLSTICK_TURNOFF |

View File

@ -7,8 +7,8 @@ Component to interface with various media players
import logging import logging
from homeassistant.components import discovery from homeassistant.components import discovery
from homeassistant.helpers.device import Device from homeassistant.helpers.entity import Entity
from homeassistant.helpers.device_component import DeviceComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_VOLUME_UP, ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_VOLUME_UP,
SERVICE_VOLUME_DOWN, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_VOLUME_DOWN, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY,
@ -126,7 +126,7 @@ SERVICE_TO_METHOD = {
def setup(hass, config): def setup(hass, config):
""" Track states and offer events for media_players. """ """ Track states and offer events for media_players. """
component = DeviceComponent( component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL, logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS) DISCOVERY_PLATFORMS)
@ -171,7 +171,7 @@ def setup(hass, config):
return True return True
class MediaPlayerDevice(Device): class MediaPlayerDevice(Entity):
""" ABC for media player devices. """ """ ABC for media player devices. """
def turn_off(self): def turn_off(self):

View File

@ -34,6 +34,11 @@ class DemoMediaPlayer(MediaPlayerDevice):
self.media_title = media_title self.media_title = media_title
self.volume = 1.0 self.volume = 1.0
@property
def should_poll(self):
""" No polling needed for a demo componentn. """
return False
@property @property
def name(self): def name(self):
""" Returns the name of the device. """ """ Returns the name of the device. """

View File

@ -0,0 +1,31 @@
"""
homeassistant.components.notify.demo
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo notification service.
"""
from homeassistant.components.notify import ATTR_TITLE, BaseNotificationService
EVENT_NOTIFY = "notify"
def get_service(hass, config):
""" Get the demo notification service. """
return DemoNotificationService(hass)
# pylint: disable=too-few-public-methods
class DemoNotificationService(BaseNotificationService):
""" Implements demo notification service. """
def __init__(self, hass):
self.hass = hass
def send_message(self, message="", **kwargs):
""" Send a message to a user. """
title = kwargs.get(ATTR_TITLE)
self.hass.bus.fire(EVENT_NOTIFY, {"title": title, "message": message})

View File

@ -0,0 +1,98 @@
"""
Pushover platform for notify component.
Configuration:
To use the Pushover notifier you will need to add something like the following
to your config/configuration.yaml
notify:
platform: pushover
api_key: ABCDEFGHJKLMNOPQRSTUVXYZ
user_key: ABCDEFGHJKLMNOPQRSTUVXYZ
VARIABLES:
api_key
*Required
This parameter is optional but should be configured, in order to get an API
key you should go to https://pushover.net and register a new application.
This is a quote from the pushover website regarding free/open source apps:
"If you are creating a client-side library, application, or open source project
that will be redistributed and installed by end-users, you may want to require
each of your users to register their own application rather than including your
own API token with the software."
When setting up the application I recommend using the icon located here:
https://home-assistant.io/images/favicon-192x192.png
user_key
*Required
To retrieve this value log into your account at https://pushover.net
"""
import logging
from homeassistant.helpers import validate_config
from homeassistant.components.notify import (
DOMAIN, ATTR_TITLE, BaseNotificationService)
from homeassistant.const import CONF_API_KEY
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-variable
def get_service(hass, config):
""" Get the pushover notification service. """
if not validate_config(config,
{DOMAIN: ['user_key', CONF_API_KEY]},
_LOGGER):
return None
try:
# pylint: disable=no-name-in-module, unused-variable
from pushover import InitError
except ImportError:
_LOGGER.exception(
"Unable to import pushover. "
"Did you maybe not install the 'python-pushover.py' package?")
return None
try:
api_token = config[DOMAIN].get(CONF_API_KEY)
return PushoverNotificationService(
config[DOMAIN]['user_key'],
api_token)
except InitError:
_LOGGER.error(
"Wrong API key supplied. "
"Get it at https://pushover.net")
# pylint: disable=too-few-public-methods
class PushoverNotificationService(BaseNotificationService):
""" Implements notification service for Pushover. """
def __init__(self, user_key, api_token):
# pylint: disable=no-name-in-module, unused-variable
from pushover import Client
self._user_key = user_key
self._api_token = api_token
self.pushover = Client(
self._user_key, api_token=self._api_token)
def send_message(self, message="", **kwargs):
""" Send a message to a user. """
# pylint: disable=no-name-in-module
from pushover import RequestError
title = kwargs.get(ATTR_TITLE)
try:
self.pushover.send_message(message, title=title)
except RequestError:
_LOGGER.exception("Could not send pushover notification")

View File

@ -262,7 +262,7 @@ class Recorder(threading.Thread):
def _setup_connection(self): def _setup_connection(self):
""" Ensure database is ready to fly. """ """ Ensure database is ready to fly. """
db_path = self.hass.get_config_path(DB_FILE) db_path = self.hass.config.path(DB_FILE)
self.conn = sqlite3.connect(db_path, check_same_thread=False) self.conn = sqlite3.connect(db_path, check_same_thread=False)
self.conn.row_factory = sqlite3.Row self.conn.row_factory = sqlite3.Row

View File

@ -0,0 +1,190 @@
"""
homeassistant.components.scene
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows users to set and activate scenes within Home Assistant.
A scene is a set of states that describe how you want certain entities to be.
For example, light A should be red with 100 brightness. Light B should be on.
A scene is active if all states of the scene match the real states.
If a scene is manually activated it will store the previous state of the
entities. These will be restored when the state is deactivated manually.
If one of the enties that are being tracked change state on its own, the
old state will not be restored when it is being deactivated.
"""
import logging
from collections import namedtuple
from homeassistant import State
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.state import reproduce_state
from homeassistant.const import (
ATTR_ENTITY_ID, STATE_OFF, STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF)
DOMAIN = 'scene'
DEPENDENCIES = ['group']
ATTR_ACTIVE_REQUESTED = "active_requested"
CONF_ENTITIES = "entities"
SceneConfig = namedtuple('SceneConfig', ['name', 'states'])
def setup(hass, config):
""" Sets up scenes. """
logger = logging.getLogger(__name__)
scene_configs = config.get(DOMAIN)
if not isinstance(scene_configs, list):
logger.error('Scene config should be a list of scenes')
return False
component = EntityComponent(logger, DOMAIN, hass)
component.add_entities(Scene(hass, _process_config(scene_config))
for scene_config in scene_configs)
def handle_scene_service(service):
""" Handles calls to the switch services. """
target_scenes = component.extract_from_service(service)
for scene in target_scenes:
if service.service == SERVICE_TURN_ON:
scene.turn_on()
else:
scene.turn_off()
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_scene_service)
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service)
return True
def _process_config(scene_config):
""" Process passed in config into a format to work with. """
name = scene_config.get('name')
states = {}
c_entities = dict(scene_config.get(CONF_ENTITIES, {}))
for entity_id in c_entities:
if isinstance(c_entities[entity_id], dict):
state = c_entities[entity_id].pop('state', None)
attributes = c_entities[entity_id]
else:
state = c_entities[entity_id]
attributes = {}
# YAML translates 'on' to a boolean
# http://yaml.org/type/bool.html
if isinstance(state, bool):
state = STATE_ON if state else STATE_OFF
else:
state = str(state)
states[entity_id.lower()] = State(entity_id, state, attributes)
return SceneConfig(name, states)
class Scene(ToggleEntity):
""" A scene is a group of entities and the states we want them to be. """
def __init__(self, hass, scene_config):
self.hass = hass
self.scene_config = scene_config
self.is_active = False
self.prev_states = None
self.ignore_updates = False
self.hass.states.track_change(
self.entity_ids, self.entity_state_changed)
self.update()
@property
def should_poll(self):
return False
@property
def name(self):
return self.scene_config.name
@property
def is_on(self):
return self.is_active
@property
def entity_ids(self):
""" Entity IDs part of this scene. """
return self.scene_config.states.keys()
@property
def state_attributes(self):
""" Scene state attributes. """
return {
ATTR_ENTITY_ID: list(self.entity_ids),
ATTR_ACTIVE_REQUESTED: self.prev_states is not None,
}
def turn_on(self):
""" Activates scene. Tries to get entities into requested state. """
self.prev_states = tuple(self.hass.states.get(entity_id)
for entity_id in self.entity_ids)
self._reproduce_state(self.scene_config.states.values())
def turn_off(self):
""" Deactivates scene and restores old states. """
if self.prev_states:
self._reproduce_state(self.prev_states)
self.prev_states = None
def entity_state_changed(self, entity_id, old_state, new_state):
""" Called when an entity part of this scene changes state. """
if self.ignore_updates:
return
# If new state is not what we expect, it can never be active
if self._state_as_requested(new_state):
self.update()
else:
self.is_active = False
self.prev_states = None
self.update_ha_state()
def update(self):
"""
Update if the scene is active.
Will look at each requested state and see if the current entity
has the same state and has at least the same attributes with the
same values. The real state can have more attributes.
"""
self.is_active = all(
self._state_as_requested(self.hass.states.get(entity_id))
for entity_id in self.entity_ids)
def _state_as_requested(self, cur_state):
""" Returns if given state is as requested. """
state = self.scene_config.states.get(cur_state and cur_state.entity_id)
return (cur_state is not None and state.state == cur_state.state and
all(value == cur_state.attributes.get(key)
for key, value in state.attributes.items()))
def _reproduce_state(self, states):
""" Wraps reproduce state with Scence specific logic. """
self.ignore_updates = True
reproduce_state(self.hass, states, True)
self.ignore_updates = False
self.update_ha_state(True)

View File

@ -35,9 +35,6 @@ _SCHEDULE_FILE = 'schedule.json'
def setup(hass, config): def setup(hass, config):
""" Create the schedules """ """ Create the schedules """
if DOMAIN in hass.components:
return True
def setup_listener(schedule, event_data): def setup_listener(schedule, event_data):
""" Creates the event listener based on event_data """ """ Creates the event listener based on event_data """
event_type = event_data['type'] event_type = event_data['type']
@ -47,9 +44,7 @@ def setup(hass, config):
if event_type in ['time']: if event_type in ['time']:
component = 'scheduler.{}'.format(event_type) component = 'scheduler.{}'.format(event_type)
elif component not in hass.components and \ elif not bootstrap.setup_component(hass, component, config):
not bootstrap.setup_component(hass, component, config):
_LOGGER.warn("Could setup event listener for %s", component) _LOGGER.warn("Could setup event listener for %s", component)
return None return None
@ -74,7 +69,7 @@ def setup(hass, config):
schedule.schedule(hass) schedule.schedule(hass)
return True return True
with open(hass.get_config_path(_SCHEDULE_FILE)) as schedule_file: with open(hass.config.path(_SCHEDULE_FILE)) as schedule_file:
schedule_descriptions = json.load(schedule_file) schedule_descriptions = json.load(schedule_file)
for schedule_description in schedule_descriptions: for schedule_description in schedule_descriptions:

View File

@ -0,0 +1,140 @@
"""
homeassistant.components.script
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Scripts are a sequence of actions that can be triggered manually
by the user or automatically based upon automation events, etc.
"""
import logging
from datetime import datetime, timedelta
import threading
from homeassistant.util import split_entity_id
from homeassistant.const import (
STATE_ON, STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF, EVENT_TIME_CHANGED)
DOMAIN = "script"
DEPENDENCIES = ["group"]
CONF_ALIAS = "alias"
CONF_SERVICE = "execute_service"
CONF_SERVICE_DATA = "service_data"
CONF_SEQUENCE = "sequence"
CONF_DELAY = "delay"
_LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Load the scripts from the configuration. """
scripts = []
for name, cfg in config[DOMAIN].items():
if CONF_SEQUENCE not in cfg:
_LOGGER.warn("Missing key 'sequence' for script %s", name)
continue
alias = cfg.get(CONF_ALIAS, name)
entity_id = "{}.{}".format(DOMAIN, name)
script = Script(hass, entity_id, alias, cfg[CONF_SEQUENCE])
hass.services.register(DOMAIN, name, script)
scripts.append(script)
def turn_on(service):
""" Calls a script. """
for entity_id in service.data['entity_id']:
domain, service = split_entity_id(entity_id)
hass.services.call(domain, service, {})
def turn_off(service):
""" Cancels a script. """
for entity_id in service.data['entity_id']:
for script in scripts:
if script.entity_id == entity_id:
script.cancel()
hass.services.register(DOMAIN, SERVICE_TURN_ON, turn_on)
hass.services.register(DOMAIN, SERVICE_TURN_OFF, turn_off)
return True
class Script(object):
# pylint: disable=attribute-defined-outside-init
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-few-public-methods
"""
A script contains a sequence of service calls or configured delays
that are executed in order.
Each script also has a state (on/off) indicating whether the script is
running or not.
"""
def __init__(self, hass, entity_id, alias, sequence):
self.hass = hass
self.alias = alias
self.sequence = sequence
self.entity_id = entity_id
self._lock = threading.Lock()
self._reset()
def cancel(self):
""" Cancels a running script and resets the state back to off. """
_LOGGER.info("Cancelled script %s", self.alias)
with self._lock:
if self.listener:
self.hass.bus.remove_listener(EVENT_TIME_CHANGED,
self.listener)
self.listener = None
self._reset()
def _reset(self):
""" Resets a script back to default state so that it is ready to
run from the start again. """
self.actions = None
self.listener = None
self.last_action = "Not Running"
self.hass.states.set(self.entity_id, STATE_OFF, {
"friendly_name": self.alias,
"last_action": self.last_action
})
def _execute_until_done(self):
""" Executes a sequence of actions until finished or until a delay
is encountered. If a delay action is encountered, the script
registers itself to be called again in the future, when
_execute_until_done will resume.
Returns True if finished, False otherwise. """
for action in self.actions:
if CONF_SERVICE in action:
self._call_service(action)
elif CONF_DELAY in action:
delay = timedelta(**action[CONF_DELAY])
point_in_time = datetime.now() + delay
self.listener = self.hass.track_point_in_time(
self, point_in_time)
return False
return True
def __call__(self, *args, **kwargs):
""" Executes the script. """
_LOGGER.info("Executing script %s", self.alias)
with self._lock:
if self.actions is None:
self.actions = (action for action in self.sequence)
if not self._execute_until_done():
state = self.hass.states.get(self.entity_id)
state.attributes['last_action'] = self.last_action
self.hass.states.set(self.entity_id, STATE_ON,
state.attributes)
else:
self._reset()
def _call_service(self, action):
""" Calls the service specified in the action. """
self.last_action = action.get(CONF_ALIAS, action[CONF_SERVICE])
_LOGGER.info("Executing script %s step %s", self.alias,
self.last_action)
domain, service = split_entity_id(action[CONF_SERVICE])
data = action.get(CONF_SERVICE_DATA, {})
self.hass.services.call(domain, service, data)

View File

@ -5,7 +5,7 @@ Component to interface with various sensors that can be monitored.
""" """
import logging import logging
from homeassistant.helpers.device_component import DeviceComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components import wink, zwave from homeassistant.components import wink, zwave
DOMAIN = 'sensor' DOMAIN = 'sensor'
@ -23,7 +23,7 @@ DISCOVERY_PLATFORMS = {
def setup(hass, config): def setup(hass, config):
""" Track states and offer events for sensors. """ """ Track states and offer events for sensors. """
component = DeviceComponent( component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL, logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS) DISCOVERY_PLATFORMS)

View File

@ -1,25 +1,30 @@
""" Support for Wink sensors. """ """ Support for Wink sensors. """
from homeassistant.helpers.device import Device from homeassistant.helpers.entity import Entity
from homeassistant.const import ( from homeassistant.const import TEMP_CELCIUS, ATTR_BATTERY_LEVEL
TEMP_CELCIUS, ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME)
# pylint: disable=unused-argument # pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo sensors. """ """ Sets up the Demo sensors. """
add_devices([ add_devices([
DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS), DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS, 12),
DemoSensor('Outside Humidity', 54, '%'), DemoSensor('Outside Humidity', 54, '%', None),
]) ])
class DemoSensor(Device): class DemoSensor(Entity):
""" A Demo sensor. """ """ A Demo sensor. """
def __init__(self, name, state, unit_of_measurement): def __init__(self, name, state, unit_of_measurement, battery):
self._name = name self._name = name
self._state = state self._state = state
self._unit_of_measurement = unit_of_measurement self._unit_of_measurement = unit_of_measurement
self._battery = battery
@property
def should_poll(self):
""" No polling needed for a demo sensor. """
return False
@property @property
def name(self): def name(self):
@ -31,10 +36,15 @@ class DemoSensor(Device):
""" Returns the state of the device. """ """ Returns the state of the device. """
return self._state return self._state
@property
def unit_of_measurement(self):
""" Unit this state is expressed in. """
return self._unit_of_measurement
@property @property
def state_attributes(self): def state_attributes(self):
""" Returns the state attributes. """ """ Returns the state attributes. """
return { if self._battery:
ATTR_FRIENDLY_NAME: self._name, return {
ATTR_UNIT_OF_MEASUREMENT: self._unit_of_measurement, ATTR_BATTERY_LEVEL: self._battery,
} }

View File

@ -0,0 +1,163 @@
"""
homeassistant.components.sensor.sabnzbd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Monitors SABnzbd NZB client API
Configuration:
To use the SABnzbd sensor you will need to add something like the following to
your config/configuration.yaml
sensor:
platform: sabnzbd
name: SAB
api_key: YOUR_API_KEY
base_url: YOUR_SABNZBD_BASE_URL
monitored_variables:
- type: 'current_status'
- type: 'speed'
- type: 'queue_size'
- type: 'queue_remaining'
- type: 'disk_size'
- type: 'disk_free'
VARIABLES:
base_url
*Required
This is the base URL of your SABnzbd instance including the port number if not
running on 80
Example: http://192.168.1.32:8124/
name
*Optional
The name to use when displaying this SABnzbd instance
monitored_variables
*Required
An array specifying the variables to monitor.
These are the variables for the monitored_variables array:
type
*Required
The variable you wish to monitor, see the configuration example above for a
list of all available variables
"""
from homeassistant.util import Throttle
from datetime import timedelta
from homeassistant.helpers.entity import Entity
# pylint: disable=no-name-in-module, import-error
from homeassistant.external.nzbclients.sabnzbd import SabnzbdApi
from homeassistant.external.nzbclients.sabnzbd import SabnzbdApiException
import logging
SENSOR_TYPES = {
'current_status': ['Status', ''],
'speed': ['Speed', 'MB/s'],
'queue_size': ['Queue', 'MB'],
'queue_remaining': ['Left', 'MB'],
'disk_size': ['Disk', 'GB'],
'disk_free': ['Disk Free', 'GB'],
}
_LOGGER = logging.getLogger(__name__)
_THROTTLED_REFRESH = None
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the sensors """
api_key = config.get("api_key")
base_url = config.get("base_url")
name = config.get("name", "SABnzbd")
if not base_url:
_LOGGER.error('Missing config variable base_url')
return False
if not api_key:
_LOGGER.error('Missing config variable api_key')
return False
sab_api = SabnzbdApi(base_url, api_key)
try:
sab_api.check_available()
except SabnzbdApiException:
_LOGGER.exception("Connection to SABnzbd API failed.")
return False
# pylint: disable=global-statement
global _THROTTLED_REFRESH
_THROTTLED_REFRESH = Throttle(timedelta(seconds=1))(sab_api.refresh_queue)
dev = []
for variable in config['monitored_variables']:
if variable['type'] not in SENSOR_TYPES:
_LOGGER.error('Sensor type: "%s" does not exist', variable['type'])
else:
dev.append(SabnzbdSensor(variable['type'], sab_api, name))
add_devices(dev)
class SabnzbdSensor(Entity):
""" A Sabnzbd sensor """
def __init__(self, sensor_type, sabnzb_client, client_name):
self._name = SENSOR_TYPES[sensor_type][0]
self.sabnzb_client = sabnzb_client
self.type = sensor_type
self.client_name = client_name
self._state = None
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
@property
def name(self):
return self.client_name + ' ' + self._name
@property
def state(self):
""" Returns the state of the device. """
return self._state
@property
def unit_of_measurement(self):
""" Unit of measurement of this entity, if any. """
return self._unit_of_measurement
def refresh_sabnzbd_data(self):
""" Calls the throttled SABnzbd refresh method. """
if _THROTTLED_REFRESH is not None:
try:
_THROTTLED_REFRESH()
except SabnzbdApiException:
_LOGGER.exception(
self.name + " Connection to SABnzbd API failed."
)
def update(self):
self.refresh_sabnzbd_data()
if self.sabnzb_client.queue:
if self.type == 'current_status':
self._state = self.sabnzb_client.queue.get('status')
elif self.type == 'speed':
mb_spd = float(self.sabnzb_client.queue.get('kbpersec')) / 1024
self._state = round(mb_spd, 1)
elif self.type == 'queue_size':
self._state = self.sabnzb_client.queue.get('mb')
elif self.type == 'queue_remaining':
self._state = self.sabnzb_client.queue.get('mbleft')
elif self.type == 'disk_size':
self._state = self.sabnzb_client.queue.get('diskspacetotal1')
elif self.type == 'disk_free':
self._state = self.sabnzb_client.queue.get('diskspace1')
else:
self._state = 'Unknown'

View File

@ -6,9 +6,8 @@ Shows system monitor values such as: disk, memory and processor use
""" """
from homeassistant.helpers.device import Device from homeassistant.helpers.entity import Entity
from homeassistant.const import ( from homeassistant.const import STATE_ON, STATE_OFF
ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME, STATE_ON, STATE_OFF)
import psutil import psutil
import logging import logging
@ -43,7 +42,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(dev) add_devices(dev)
class SystemMonitorSensor(Device): class SystemMonitorSensor(Entity):
""" A system monitor sensor """ """ A system monitor sensor """
def __init__(self, sensor_type, argument=''): def __init__(self, sensor_type, argument=''):
@ -51,7 +50,7 @@ class SystemMonitorSensor(Device):
self.argument = argument self.argument = argument
self.type = sensor_type self.type = sensor_type
self._state = None self._state = None
self.unit_of_measurement = SENSOR_TYPES[sensor_type][1] self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self.update() self.update()
@property @property
@ -64,12 +63,8 @@ class SystemMonitorSensor(Device):
return self._state return self._state
@property @property
def state_attributes(self): def unit_of_measurement(self):
""" Returns the state attributes. """ return self._unit_of_measurement
return {
ATTR_FRIENDLY_NAME: self.name,
ATTR_UNIT_OF_MEASUREMENT: self.unit_of_measurement,
}
def update(self): def update(self):
if self.type == 'disk_use_percent': if self.type == 'disk_use_percent':

View File

@ -29,9 +29,9 @@ from collections import namedtuple
import tellcore.telldus as telldus import tellcore.telldus as telldus
import tellcore.constants as tellcore_constants import tellcore.constants as tellcore_constants
from homeassistant.const import ( from homeassistant.const import TEMP_CELCIUS
ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, TEMP_CELCIUS) from homeassistant.helpers.entity import Entity
from homeassistant.helpers.device import Device import homeassistant.util as util
DatatypeDescription = namedtuple("DatatypeDescription", ['name', 'unit']) DatatypeDescription = namedtuple("DatatypeDescription", ['name', 'unit'])
@ -71,18 +71,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
return return
sensors = [] sensors = []
datatype_mask = util.convert(config.get('datatype_mask'), int, 127)
for ts_sensor in core.sensors(): for ts_sensor in core.sensors():
try: try:
sensor_name = config[str(ts_sensor.id)] sensor_name = config[ts_sensor.id]
except KeyError: except KeyError:
if 'only_named' in config: if 'only_named' in config:
continue continue
sensor_name = str(ts_sensor.id) sensor_name = str(ts_sensor.id)
for datatype in sensor_value_descriptions.keys(): for datatype in sensor_value_descriptions.keys():
if datatype & int(config['datatype_mask']) and \ if datatype & datatype_mask and ts_sensor.has_value(datatype):
ts_sensor.has_value(datatype):
sensor_info = sensor_value_descriptions[datatype] sensor_info = sensor_value_descriptions[datatype]
@ -93,13 +93,13 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(sensors) add_devices(sensors)
class TellstickSensor(Device): class TellstickSensor(Entity):
""" Represents a Tellstick sensor. """ """ Represents a Tellstick sensor. """
def __init__(self, name, sensor, datatype, sensor_info): def __init__(self, name, sensor, datatype, sensor_info):
self.datatype = datatype self.datatype = datatype
self.sensor = sensor self.sensor = sensor
self.unit = sensor_info.unit or None self._unit_of_measurement = sensor_info.unit or None
self._name = "{} {}".format(name, sensor_info.name) self._name = "{} {}".format(name, sensor_info.name)
@ -114,13 +114,5 @@ class TellstickSensor(Device):
return self.sensor.value(self.datatype).value return self.sensor.value(self.datatype).value
@property @property
def state_attributes(self): def unit_of_measurement(self):
""" Returns the state attributes. """ return self._unit_of_measurement
attrs = {
ATTR_FRIENDLY_NAME: self._name,
}
if self.unit:
attrs[ATTR_UNIT_OF_MEASUREMENT] = self.unit
return attrs

View File

@ -50,7 +50,7 @@ import logging
import time import time
from requests.exceptions import RequestException from requests.exceptions import RequestException
from homeassistant.helpers import Device from homeassistant.helpers.entity import Entity
from homeassistant.const import ( from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME) ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME)
# pylint: disable=no-name-in-module, import-error # pylint: disable=no-name-in-module, import-error
@ -99,7 +99,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(get_devices(hass, config)) add_devices(get_devices(hass, config))
class VeraSensor(Device): class VeraSensor(Entity):
""" Represents a Vera Sensor """ """ Represents a Vera Sensor """
def __init__(self, vera_device, extra_data=None): def __init__(self, vera_device, extra_data=None):

View File

@ -4,8 +4,8 @@ import logging
# pylint: disable=no-name-in-module, import-error # pylint: disable=no-name-in-module, import-error
import homeassistant.external.wink.pywink as pywink import homeassistant.external.wink.pywink as pywink
from homeassistant.components.wink import WinkSensorDevice from homeassistant.helpers.entity import Entity
from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.const import CONF_ACCESS_TOKEN, STATE_OPEN, STATE_CLOSED
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
@ -22,3 +22,34 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
pywink.set_bearer_token(token) pywink.set_bearer_token(token)
add_devices(WinkSensorDevice(sensor) for sensor in pywink.get_sensors()) add_devices(WinkSensorDevice(sensor) for sensor in pywink.get_sensors())
class WinkSensorDevice(Entity):
""" represents a wink sensor within home assistant. """
def __init__(self, wink):
self.wink = wink
@property
def state(self):
""" Returns the state. """
return STATE_OPEN if self.is_open else STATE_CLOSED
@property
def unique_id(self):
""" Returns the id of this wink sensor """
return "{}.{}".format(self.__class__, self.wink.deviceId())
@property
def name(self):
""" Returns the name of the sensor if any. """
return self.wink.name()
def update(self):
""" Update state of the sensor. """
self.wink.updateState()
@property
def is_open(self):
""" True if door is open. """
return self.wink.state()

View File

@ -9,9 +9,9 @@ from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher from pydispatch import dispatcher
import homeassistant.components.zwave as zwave import homeassistant.components.zwave as zwave
from homeassistant.helpers.device import Device from homeassistant.helpers.entity import Entity
from homeassistant.const import ( from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF, ATTR_BATTERY_LEVEL, STATE_ON, STATE_OFF,
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION) TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION)
@ -22,17 +22,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
value.set_change_verified(False) value.set_change_verified(False)
if zwave.NETWORK.controller.node_id not in node.groups[1].associations: # if 1 in groups and (zwave.NETWORK.controller.node_id not in
node.groups[1].add_association(zwave.NETWORK.controller.node_id) # groups[1].associations):
# node.groups[1].add_association(zwave.NETWORK.controller.node_id)
if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY: if value.command_class == zwave.COMMAND_CLASS_SENSOR_BINARY:
return [ZWaveBinarySensor(value)] add_devices([ZWaveBinarySensor(value)])
elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL: elif value.command_class == zwave.COMMAND_CLASS_SENSOR_MULTILEVEL:
return [ZWaveMultilevelSensor(value)] add_devices([ZWaveMultilevelSensor(value)])
class ZWaveSensor(Device): class ZWaveSensor(Entity):
""" Represents a Z-Wave sensor. """ """ Represents a Z-Wave sensor. """
def __init__(self, sensor_value): def __init__(self, sensor_value):
self._value = sensor_value self._value = sensor_value
@ -76,11 +77,6 @@ class ZWaveSensor(Device):
if battery_level is not None: if battery_level is not None:
attrs[ATTR_BATTERY_LEVEL] = battery_level attrs[ATTR_BATTERY_LEVEL] = battery_level
unit = self.unit
if unit:
attrs[ATTR_UNIT_OF_MEASUREMENT] = unit
location = self._node.location location = self._node.location
if location: if location:
@ -89,8 +85,7 @@ class ZWaveSensor(Device):
return attrs return attrs
@property @property
def unit(self): def unit_of_measurement(self):
""" Unit if sensor has one. """
return self._value.units return self._value.units
def _value_changed(self, value): def _value_changed(self, value):
@ -119,12 +114,13 @@ class ZWaveMultilevelSensor(ZWaveSensor):
if self._value.units in ('C', 'F'): if self._value.units in ('C', 'F'):
return round(value, 1) return round(value, 1)
elif isinstance(value, float):
return round(value, 2)
return value return value
@property @property
def unit(self): def unit_of_measurement(self):
""" Unit of this sensor. """
unit = self._value.units unit = self._value.units
if unit == 'C': if unit == 'C':

View File

@ -24,9 +24,6 @@ which event (sunset or sunrise) and the offset.
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
import homeassistant as ha
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.helpers import validate_config
from homeassistant.util import str_to_datetime, datetime_to_str from homeassistant.util import str_to_datetime, datetime_to_str
from homeassistant.components.scheduler import ServiceEventListener from homeassistant.components.scheduler import ServiceEventListener
@ -83,11 +80,6 @@ def setup(hass, config):
""" Tracks the state of the sun. """ """ Tracks the state of the sun. """
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
if not validate_config(config,
{ha.DOMAIN: [CONF_LATITUDE, CONF_LONGITUDE]},
logger):
return False
try: try:
import ephem import ephem
except ImportError: except ImportError:
@ -96,8 +88,8 @@ def setup(hass, config):
sun = ephem.Sun() # pylint: disable=no-member sun = ephem.Sun() # pylint: disable=no-member
latitude = str(config[ha.DOMAIN][CONF_LATITUDE]) latitude = str(hass.config.latitude)
longitude = str(config[ha.DOMAIN][CONF_LONGITUDE]) longitude = str(hass.config.longitude)
# Validate latitude and longitude # Validate latitude and longitude
observer = ephem.Observer() observer = ephem.Observer()

View File

@ -6,7 +6,7 @@ Component to interface with various switches that can be controlled remotely.
import logging import logging
from datetime import timedelta from datetime import timedelta
from homeassistant.helpers.device_component import DeviceComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.const import ( from homeassistant.const import (
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID) STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
@ -58,7 +58,7 @@ def turn_off(hass, entity_id=None):
def setup(hass, config): def setup(hass, config):
""" Track states and offer events for switches. """ """ Track states and offer events for switches. """
component = DeviceComponent( component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS, _LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_SWITCHES) GROUP_NAME_ALL_SWITCHES)
component.setup(config) component.setup(config)

View File

@ -1,5 +1,5 @@
""" Demo platform that has two fake switchces. """ """ Demo platform that has two fake switchces. """
from homeassistant.helpers.device import ToggleDevice from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME
@ -12,12 +12,17 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
]) ])
class DemoSwitch(ToggleDevice): class DemoSwitch(ToggleEntity):
""" Provides a demo switch. """ """ Provides a demo switch. """
def __init__(self, name, state): def __init__(self, name, state):
self._name = name or DEVICE_DEFAULT_NAME self._name = name or DEVICE_DEFAULT_NAME
self._state = state self._state = state
@property
def should_poll(self):
""" No polling needed for a demo switch. """
return False
@property @property
def name(self): def name(self):
""" Returns the name of the device if any. """ """ Returns the name of the device if any. """

View File

@ -3,7 +3,7 @@ import logging
from homeassistant.const import ATTR_FRIENDLY_NAME from homeassistant.const import ATTR_FRIENDLY_NAME
from homeassistant.helpers.device import ToggleDevice from homeassistant.helpers.entity import ToggleEntity
import tellcore.constants as tellcore_constants import tellcore.constants as tellcore_constants
@ -29,7 +29,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
add_devices_callback(switches) add_devices_callback(switches)
class TellstickSwitchDevice(ToggleDevice): class TellstickSwitchDevice(ToggleEntity):
""" represents a Tellstick switch within home assistant. """ """ represents a Tellstick switch within home assistant. """
last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON | last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON |
tellcore_constants.TELLSTICK_TURNOFF) tellcore_constants.TELLSTICK_TURNOFF)

View File

@ -52,7 +52,7 @@ import logging
import time import time
from requests.exceptions import RequestException from requests.exceptions import RequestException
from homeassistant.helpers import ToggleDevice from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import ( from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME) ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME)
# pylint: disable=no-name-in-module, import-error # pylint: disable=no-name-in-module, import-error
@ -100,7 +100,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(get_devices(hass, config)) add_devices(get_devices(hass, config))
class VeraSwitch(ToggleDevice): class VeraSwitch(ToggleEntity):
""" Represents a Vera Switch """ """ Represents a Vera Switch """
def __init__(self, vera_device, extra_data=None): def __init__(self, vera_device, extra_data=None):

View File

@ -1,7 +1,7 @@
""" Support for WeMo switchces. """ """ Support for WeMo switchces. """
import logging import logging
from homeassistant.helpers.device import ToggleDevice from homeassistant.helpers.entity import ToggleEntity
from homeassistant.components.switch import ( from homeassistant.components.switch import (
ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH) ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH)
@ -38,7 +38,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
if isinstance(switch, pywemo.Switch)]) if isinstance(switch, pywemo.Switch)])
class WemoSwitch(ToggleDevice): class WemoSwitch(ToggleEntity):
""" represents a WeMo switch within home assistant. """ """ represents a WeMo switch within home assistant. """
def __init__(self, wemo): def __init__(self, wemo):
self.wemo = wemo self.wemo = wemo

View File

@ -6,13 +6,12 @@ Provides functionality to interact with thermostats.
""" """
import logging import logging
from homeassistant.helpers.device_component import DeviceComponent from homeassistant.helpers.entity_component import EntityComponent
import homeassistant.util as util import homeassistant.util as util
from homeassistant.helpers.device import Device from homeassistant.helpers.entity import Entity
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT, ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_ON, STATE_OFF)
STATE_ON, STATE_OFF)
DOMAIN = "thermostat" DOMAIN = "thermostat"
DEPENDENCIES = [] DEPENDENCIES = []
@ -53,7 +52,7 @@ def set_temperature(hass, temperature, entity_id=None):
def setup(hass, config): def setup(hass, config):
""" Setup thermostats. """ """ Setup thermostats. """
component = DeviceComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL) component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
component.setup(config) component.setup(config)
def thermostat_service(service): def thermostat_service(service):
@ -99,7 +98,7 @@ def setup(hass, config):
return True return True
class ThermostatDevice(Device): class ThermostatDevice(Entity):
""" Represents a thermostat within Home Assistant. """ """ Represents a thermostat within Home Assistant. """
# pylint: disable=no-self-use # pylint: disable=no-self-use
@ -109,11 +108,6 @@ class ThermostatDevice(Device):
""" Returns the current state. """ """ Returns the current state. """
return self.target_temperature return self.target_temperature
@property
def unit_of_measurement(self):
""" Returns the unit of measurement. """
return ""
@property @property
def device_state_attributes(self): def device_state_attributes(self):
""" Returns device specific state attributes. """ """ Returns device specific state attributes. """
@ -123,8 +117,8 @@ class ThermostatDevice(Device):
def state_attributes(self): def state_attributes(self):
""" Returns optional state attributes. """ """ Returns optional state attributes. """
data = { data = {
ATTR_UNIT_OF_MEASUREMENT: self.unit_of_measurement, ATTR_CURRENT_TEMPERATURE: self.hass.config.temperature(
ATTR_CURRENT_TEMPERATURE: self.current_temperature self.current_temperature, self.unit_of_measurement)[0]
} }
is_away = self.is_away_mode_on is_away = self.is_away_mode_on

View File

@ -26,6 +26,11 @@ class DemoThermostat(ThermostatDevice):
self._away = away self._away = away
self._current_temperature = current_temperature self._current_temperature = current_temperature
@property
def should_poll(self):
""" No polling needed for a demo thermostat. """
return False
@property @property
def name(self): def name(self):
""" Returns the name. """ """ Returns the name. """

View File

@ -8,10 +8,10 @@ import homeassistant.external.wink.pywink as pywink
from homeassistant import bootstrap from homeassistant import bootstrap
from homeassistant.loader import get_component from homeassistant.loader import get_component
from homeassistant.helpers import validate_config, ToggleDevice, Device from homeassistant.helpers import validate_config
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import ( from homeassistant.const import (
EVENT_PLATFORM_DISCOVERED, CONF_ACCESS_TOKEN, EVENT_PLATFORM_DISCOVERED, CONF_ACCESS_TOKEN,
STATE_OPEN, STATE_CLOSED,
ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME) ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME)
DOMAIN = "wink" DOMAIN = "wink"
@ -41,8 +41,7 @@ def setup(hass, config):
component = get_component(component_name) component = get_component(component_name)
# Ensure component is loaded # Ensure component is loaded
if component.DOMAIN not in hass.components: bootstrap.setup_component(hass, component.DOMAIN, config)
bootstrap.setup_component(hass, component.DOMAIN, config)
# Fire discovery event # Fire discovery event
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, { hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
@ -53,45 +52,7 @@ def setup(hass, config):
return True return True
class WinkSensorDevice(Device): class WinkToggleDevice(ToggleEntity):
""" represents a wink sensor within home assistant. """
def __init__(self, wink):
self.wink = wink
@property
def state(self):
""" Returns the state. """
return STATE_OPEN if self.is_open else STATE_CLOSED
@property
def unique_id(self):
""" Returns the id of this wink switch """
return "{}.{}".format(self.__class__, self.wink.deviceId())
@property
def name(self):
""" Returns the name of the sensor if any. """
return self.wink.name()
@property
def state_attributes(self):
""" Returns optional state attributes. """
return {
ATTR_FRIENDLY_NAME: self.wink.name()
}
def update(self):
""" Update state of the sensor. """
self.wink.updateState()
@property
def is_open(self):
""" True if door is open. """
return self.wink.state()
class WinkToggleDevice(ToggleDevice):
""" represents a Wink switch within home assistant. """ """ represents a Wink switch within home assistant. """
def __init__(self, wink): def __init__(self, wink):

View File

@ -72,7 +72,7 @@ def setup(hass, config):
# Setup options # Setup options
options = ZWaveOption( options = ZWaveOption(
config[DOMAIN].get(CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH), config[DOMAIN].get(CONF_USB_STICK_PATH, DEFAULT_CONF_USB_STICK_PATH),
user_path=hass.config_dir) user_path=hass.config.config_dir)
options.set_console_output(use_debug) options.set_console_output(use_debug)
options.lock() options.lock()
@ -81,9 +81,9 @@ def setup(hass, config):
if use_debug: if use_debug:
def log_all(signal, value=None): def log_all(signal, value=None):
""" Log all the louie signals. """ """ Log all the signals. """
print("") print("")
print("LOUIE SIGNAL *****", signal) print("SIGNAL *****", signal)
if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED, if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED,
ZWaveNetwork.SIGNAL_VALUE_ADDED): ZWaveNetwork.SIGNAL_VALUE_ADDED):
pprint(_obj_to_dict(value)) pprint(_obj_to_dict(value))
@ -96,8 +96,7 @@ def setup(hass, config):
for component, discovery_service, command_ids in DISCOVERY_COMPONENTS: for component, discovery_service, command_ids in DISCOVERY_COMPONENTS:
if value.command_class in command_ids: if value.command_class in command_ids:
# Ensure component is loaded # Ensure component is loaded
if component not in hass.components: bootstrap.setup_component(hass, component, config)
bootstrap.setup_component(hass, component, config)
# Fire discovery event # Fire discovery event
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, { hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {

View File

@ -8,6 +8,9 @@ DEVICE_DEFAULT_NAME = "Unnamed Device"
# #### CONFIG #### # #### CONFIG ####
CONF_LATITUDE = "latitude" CONF_LATITUDE = "latitude"
CONF_LONGITUDE = "longitude" CONF_LONGITUDE = "longitude"
CONF_TEMPERATURE_UNIT = "temperature_unit"
CONF_NAME = "name"
CONF_TIME_ZONE = "time_zone"
CONF_PLATFORM = "platform" CONF_PLATFORM = "platform"
CONF_HOST = "host" CONF_HOST = "host"

1
homeassistant/external/nzbclients vendored Submodule

@ -0,0 +1 @@
Subproject commit f9f9ba36934f087b9c4241303b900794a7eb6c08

View File

@ -1,15 +1,13 @@
""" """
Helper methods for components within Home Assistant. Helper methods for components within Home Assistant.
""" """
from datetime import datetime
from homeassistant.loader import get_component from homeassistant.loader import get_component
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
from homeassistant.util import ensure_unique_string, slugify from homeassistant.util import ensure_unique_string, slugify
# Deprecated 3/5/2015 - Moved to homeassistant.helpers.device # Deprecated 3/5/2015 - Moved to homeassistant.helpers.entity
# pylint: disable=unused-import # pylint: disable=unused-import
from .device import Device, ToggleDevice # noqa from .entity import Entity as Device, ToggleEntity as ToggleDevice # noqa
def generate_entity_id(entity_id_format, name, current_ids=None, hass=None): def generate_entity_id(entity_id_format, name, current_ids=None, hass=None):
@ -43,25 +41,6 @@ def extract_entity_ids(hass, service):
return [ent_id for ent_id in group.expand_entity_ids(hass, service_ent_id)] return [ent_id for ent_id in group.expand_entity_ids(hass, service_ent_id)]
# 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.
"""
def __init__(self, hass):
self.hass = hass
self.states = []
def __enter__(self):
self.now = datetime.now()
return self.states
def __exit__(self, exc_type, exc_value, traceback):
self.states.extend(self.hass.states.get_since(self.now))
def validate_config(config, items, logger): def validate_config(config, items, logger):
""" """
Validates if all items are available in the configuration. Validates if all items are available in the configuration.

View File

@ -1,120 +1,10 @@
""" """
homeassistant.helpers.device Deprecated since 3/21/2015 - please use helpers.entity
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides ABC for devices in HA.
""" """
import logging
from homeassistant import NoEntitySpecifiedError # pylint: disable=unused-import
from .entity import Entity as Device, ToggleEntity as ToggleDevice # noqa
from homeassistant.const import ( logging.getLogger(__name__).warning(
ATTR_FRIENDLY_NAME, STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME) 'This file is deprecated. Please use helpers.entity')
class Device(object):
""" ABC for Home Assistant devices. """
# pylint: disable=no-self-use
hass = None
entity_id = None
@property
def should_poll(self):
"""
Return True if device has to be polled for state.
False if device pushes its state to HA.
"""
return True
@property
def unique_id(self):
""" Returns a unique id. """
return "{}.{}".format(self.__class__, id(self))
@property
def name(self):
""" Returns the name of the device. """
return self.get_name()
@property
def state(self):
""" Returns the state of the device. """
return self.get_state()
@property
def state_attributes(self):
""" Returns the state attributes. """
return {}
# DEPRECATION NOTICE:
# Device is moving from getters to properties.
# For now the new properties will call the old functions
# This will be removed in the future.
def get_name(self):
""" Returns the name of the device if any. """
return DEVICE_DEFAULT_NAME
def get_state(self):
""" Returns state of the device. """
return "Unknown"
def get_state_attributes(self):
""" Returns optional state attributes. """
return None
def update(self):
""" Retrieve latest state from the real device. """
pass
def update_ha_state(self, force_refresh=False):
"""
Updates Home Assistant with current state of device.
If force_refresh == True will update device before setting state.
"""
if self.hass is None:
raise RuntimeError("Attribute hass is None for {}".format(self))
if self.entity_id is None:
raise NoEntitySpecifiedError(
"No entity specified for device {}".format(self.name))
if force_refresh:
self.update()
attr = self.state_attributes or {}
if ATTR_FRIENDLY_NAME not in attr and self.name:
attr[ATTR_FRIENDLY_NAME] = self.name
return self.hass.states.set(self.entity_id, self.state, attr)
def __eq__(self, other):
return (isinstance(other, Device) and
other.unique_id == self.unique_id)
def __repr__(self):
return "<Device {}: {}>".format(self.name, self.state)
class ToggleDevice(Device):
""" ABC for devices that can be turned on and off. """
# pylint: disable=no-self-use
@property
def state(self):
""" Returns the state. """
return STATE_ON if self.is_on else STATE_OFF
@property
def is_on(self):
""" True if device is on. """
return False
def turn_on(self, **kwargs):
""" Turn the device on. """
pass
def turn_off(self, **kwargs):
""" Turn the device off. """
pass

View File

@ -1,141 +1,10 @@
""" """
Provides helpers for components that handle devices. Deprecated since 3/21/2015 - please use helpers.entity_component
""" """
from homeassistant.loader import get_component import logging
from homeassistant.helpers import (
generate_entity_id, config_per_platform, extract_entity_ids)
from homeassistant.components import group, discovery
from homeassistant.const import ATTR_ENTITY_ID
# pylint: disable=unused-import
from .entity_component import EntityComponent as DeviceComponent # noqa
class DeviceComponent(object): logging.getLogger(__name__).warning(
# pylint: disable=too-many-instance-attributes 'This file is deprecated. Please use helpers.entity_component')
# pylint: disable=too-many-arguments
"""
Helper class that will help a device component manage its devices.
"""
def __init__(self, logger, domain, hass, scan_interval,
discovery_platforms=None, group_name=None):
self.logger = logger
self.hass = hass
self.domain = domain
self.entity_id_format = domain + '.{}'
self.scan_interval = scan_interval
self.discovery_platforms = discovery_platforms
self.group_name = group_name
self.devices = {}
self.group = None
self.is_polling = False
def setup(self, config):
"""
Sets up a full device component:
- Loads the platforms from the config
- Will update devices on an interval
- Will listen for supported discovered platforms
"""
# only setup group if name is given
if self.group_name is None:
self.group = None
else:
self.group = group.Group(self.hass, self.group_name,
user_defined=False)
# Look in config for Domain, Domain 2, Domain 3 etc and load them
for p_type, p_config in \
config_per_platform(config, self.domain, self.logger):
self._setup_platform(p_type, p_config)
if self.discovery_platforms:
discovery.listen(self.hass, self.discovery_platforms.keys(),
self._device_discovered)
def extract_from_service(self, service):
"""
Takes a service and extracts all known devices.
Will return all if no entity IDs given in service.
"""
if ATTR_ENTITY_ID not in service.data:
return self.devices.values()
else:
return [self.devices[entity_id] for entity_id
in extract_entity_ids(self.hass, service)
if entity_id in self.devices]
def _update_device_states(self, now):
""" Update the states of all the lights. """
self.logger.info("Updating %s states", self.domain)
for device in self.devices.values():
if device.should_poll:
device.update_ha_state(True)
def _device_discovered(self, service, info):
""" Called when a device is discovered. """
if service not in self.discovery_platforms:
return
self._setup_platform(self.discovery_platforms[service], {}, info)
def _add_devices(self, new_devices):
"""
Takes in a list of new devices. For each device will see if it already
exists. If not, will add it, set it up and push the first state.
"""
for device in new_devices:
if device is not None and device not in self.devices.values():
device.hass = self.hass
device.entity_id = generate_entity_id(
self.entity_id_format, device.name, self.devices.keys())
self.devices[device.entity_id] = device
device.update_ha_state()
if self.group is not None:
self.group.update_tracked_entity_ids(self.devices.keys())
self._start_polling()
def _start_polling(self):
""" Start polling device states if necessary. """
if self.is_polling or \
not any(device.should_poll for device in self.devices.values()):
return
self.is_polling = True
self.hass.track_time_change(
self._update_device_states,
second=range(0, 60, self.scan_interval))
def _setup_platform(self, platform_type, config, discovery_info=None):
""" Tries to setup a platform for this component. """
platform_name = '{}.{}'.format(self.domain, platform_type)
platform = get_component(platform_name)
if platform is None:
self.logger.error('Unable to find platform %s', platform_type)
return
try:
platform.setup_platform(
self.hass, config, self._add_devices, discovery_info)
except AttributeError:
# Support old deprecated method for now - 3/1/2015
if hasattr(platform, 'get_devices'):
self.logger.warning(
"Please upgrade %s to return new devices using "
"setup_platform. See %s/demo.py for an example.",
platform_name, self.domain)
self._add_devices(platform.get_devices(self.hass, config))
else:
# AttributeError if setup_platform does not exist
self.logger.exception(
"Error setting up %s", platform_type)

View File

@ -0,0 +1,139 @@
"""
homeassistant.helpers.entity
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides ABC for entities in HA.
"""
from homeassistant import NoEntitySpecifiedError
from homeassistant.const import (
ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF,
DEVICE_DEFAULT_NAME, TEMP_CELCIUS, TEMP_FAHRENHEIT)
class Entity(object):
""" ABC for Home Assistant entities. """
# pylint: disable=no-self-use
hass = None
entity_id = None
@property
def should_poll(self):
"""
Return True if entity has to be polled for state.
False if entity pushes its state to HA.
"""
return True
@property
def unique_id(self):
""" Returns a unique id. """
return "{}.{}".format(self.__class__, id(self))
@property
def name(self):
""" Returns the name of the entity. """
return self.get_name()
@property
def state(self):
""" Returns the state of the entity. """
return self.get_state()
@property
def state_attributes(self):
""" Returns the state attributes. """
return {}
@property
def unit_of_measurement(self):
""" Unit of measurement of this entity, if any. """
return None
# DEPRECATION NOTICE:
# Device is moving from getters to properties.
# For now the new properties will call the old functions
# This will be removed in the future.
def get_name(self):
""" Returns the name of the entity if any. """
return DEVICE_DEFAULT_NAME
def get_state(self):
""" Returns state of the entity. """
return "Unknown"
def get_state_attributes(self):
""" Returns optional state attributes. """
return None
def update(self):
""" Retrieve latest state. """
pass
def update_ha_state(self, force_refresh=False):
"""
Updates Home Assistant with current state of entity.
If force_refresh == True will update entity before setting state.
"""
if self.hass is None:
raise RuntimeError("Attribute hass is None for {}".format(self))
if self.entity_id is None:
raise NoEntitySpecifiedError(
"No entity id specified for entity {}".format(self.name))
if force_refresh:
self.update()
state = str(self.state)
attr = self.state_attributes or {}
if ATTR_FRIENDLY_NAME not in attr and self.name:
attr[ATTR_FRIENDLY_NAME] = self.name
if ATTR_UNIT_OF_MEASUREMENT not in attr and self.unit_of_measurement:
attr[ATTR_UNIT_OF_MEASUREMENT] = self.unit_of_measurement
# Convert temperature if we detect one
if attr.get(ATTR_UNIT_OF_MEASUREMENT) in (TEMP_CELCIUS,
TEMP_FAHRENHEIT):
state, attr[ATTR_UNIT_OF_MEASUREMENT] = \
self.hass.config.temperature(
state, attr[ATTR_UNIT_OF_MEASUREMENT])
state = str(state)
return self.hass.states.set(self.entity_id, state, attr)
def __eq__(self, other):
return (isinstance(other, Entity) and
other.unique_id == self.unique_id)
def __repr__(self):
return "<Entity {}: {}>".format(self.name, self.state)
class ToggleEntity(Entity):
""" ABC for entities that can be turned on and off. """
# pylint: disable=no-self-use
@property
def state(self):
""" Returns the state. """
return STATE_ON if self.is_on else STATE_OFF
@property
def is_on(self):
""" True if entity is on. """
return False
def turn_on(self, **kwargs):
""" Turn the entity on. """
pass
def turn_off(self, **kwargs):
""" Turn the entity off. """
pass

View File

@ -0,0 +1,149 @@
"""
homeassistant.helpers.entity_component
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides helpers for components that manage entities.
"""
from homeassistant.loader import get_component
from homeassistant.helpers import (
generate_entity_id, config_per_platform, extract_entity_ids)
from homeassistant.components import group, discovery
from homeassistant.const import ATTR_ENTITY_ID
DEFAULT_SCAN_INTERVAL = 15
class EntityComponent(object):
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-arguments
"""
Helper class that will help a component manage its entities.
"""
def __init__(self, logger, domain, hass,
scan_interval=DEFAULT_SCAN_INTERVAL,
discovery_platforms=None, group_name=None):
self.logger = logger
self.hass = hass
self.domain = domain
self.entity_id_format = domain + '.{}'
self.scan_interval = scan_interval
self.discovery_platforms = discovery_platforms
self.group_name = group_name
self.entities = {}
self.group = None
self.is_polling = False
def setup(self, config):
"""
Sets up a full entity component:
- Loads the platforms from the config
- Will listen for supported discovered platforms
"""
# Look in config for Domain, Domain 2, Domain 3 etc and load them
for p_type, p_config in \
config_per_platform(config, self.domain, self.logger):
self._setup_platform(p_type, p_config)
if self.discovery_platforms:
discovery.listen(self.hass, self.discovery_platforms.keys(),
self._entity_discovered)
def add_entities(self, new_entities):
"""
Takes in a list of new entities. For each entity will see if it already
exists. If not, will add it, set it up and push the first state.
"""
for entity in new_entities:
if entity is not None and entity not in self.entities.values():
entity.hass = self.hass
entity.entity_id = generate_entity_id(
self.entity_id_format, entity.name, self.entities.keys())
self.entities[entity.entity_id] = entity
entity.update_ha_state()
if self.group is None and self.group_name is not None:
self.group = group.Group(self.hass, self.group_name,
user_defined=False)
if self.group is not None:
self.group.update_tracked_entity_ids(self.entities.keys())
self._start_polling()
def extract_from_service(self, service):
"""
Takes a service and extracts all known entities.
Will return all if no entity IDs given in service.
"""
if ATTR_ENTITY_ID not in service.data:
return self.entities.values()
else:
return [self.entities[entity_id] for entity_id
in extract_entity_ids(self.hass, service)
if entity_id in self.entities]
def _update_entity_states(self, now):
""" Update the states of all the entities. """
self.logger.info("Updating %s entities", self.domain)
for entity in self.entities.values():
if entity.should_poll:
entity.update_ha_state(True)
def _entity_discovered(self, service, info):
""" Called when a entity is discovered. """
if service not in self.discovery_platforms:
return
self._setup_platform(self.discovery_platforms[service], {}, info)
def _start_polling(self):
""" Start polling entities if necessary. """
if self.is_polling or \
not any(entity.should_poll for entity in self.entities.values()):
return
self.is_polling = True
self.hass.track_time_change(
self._update_entity_states,
second=range(0, 60, self.scan_interval))
def _setup_platform(self, platform_type, config, discovery_info=None):
""" Tries to setup a platform for this component. """
platform_name = '{}.{}'.format(self.domain, platform_type)
platform = get_component(platform_name)
if platform is None:
self.logger.error('Unable to find platform %s', platform_type)
return
try:
platform.setup_platform(
self.hass, config, self.add_entities, discovery_info)
self.hass.config.components.append(platform_name)
except AttributeError:
# AttributeError if setup_platform does not exist
# Support old deprecated method for now - 3/1/2015
if hasattr(platform, 'get_devices'):
self.logger.warning(
"Please upgrade %s to return new entities using "
"setup_platform. See %s/demo.py for an example.",
platform_name, self.domain)
self.add_entities(platform.get_devices(self.hass, config))
else:
self.logger.exception(
"Error while setting up platform %s", platform_type)
except Exception: # pylint: disable=broad-except
self.logger.exception(
"Error while setting up platform %s", platform_type)

View File

@ -0,0 +1,58 @@
"""
homeassistant.helpers.state
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Helpers that help with state related things.
"""
import logging
from datetime import datetime
from homeassistant import State
from homeassistant.const import (
STATE_ON, STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
_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.
"""
def __init__(self, hass):
self.hass = hass
self.states = []
def __enter__(self):
self.now = datetime.now()
return self.states
def __exit__(self, exc_type, exc_value, traceback):
self.states.extend(self.hass.states.get_since(self.now))
def reproduce_state(hass, states, blocking=False):
""" Takes in a state and will try to have the entity reproduce it. """
if isinstance(states, State):
states = [states]
for state in states:
current_state = hass.states.get(state.entity_id)
if current_state is None:
continue
if state.state == STATE_ON:
service = SERVICE_TURN_ON
elif state.state == STATE_OFF:
service = SERVICE_TURN_OFF
else:
_LOGGER.warning("Unable to reproduce state for %s", state)
continue
service_data = dict(state.attributes)
service_data[ATTR_ENTITY_ID] = state.entity_id
hass.services.call(state.domain, service, service_data, blocking)

View File

@ -46,11 +46,11 @@ def prepare(hass):
pkgutil.iter_modules(components.__path__, 'homeassistant.components.')) pkgutil.iter_modules(components.__path__, 'homeassistant.components.'))
# Look for available custom components # Look for available custom components
custom_path = hass.get_config_path("custom_components") custom_path = hass.config.path("custom_components")
if os.path.isdir(custom_path): if os.path.isdir(custom_path):
# Ensure we can load custom components using Pythons import # Ensure we can load custom components using Pythons import
sys.path.insert(0, hass.config_dir) sys.path.insert(0, hass.config.config_dir)
# We cannot use the same approach as for built-in components because # We cannot use the same approach as for built-in components because
# custom components might only contain a platform for a component. # custom components might only contain a platform for a component.
@ -157,28 +157,19 @@ def load_order_components(components):
""" """
_check_prepared() _check_prepared()
group = get_component('group')
recorder = get_component('recorder')
load_order = OrderedSet() load_order = OrderedSet()
# Sort the list of modules on if they depend on group component or not. # Sort the list of modules on if they depend on group component or not.
# We do this because the components that do not depend on the group # Components that do not depend on the group usually set up states.
# component usually set up states that the group component requires to be # Components that depend on group usually use states in their setup.
# created before it can group them.
# This does not matter in the future if we can setup groups without the
# states existing yet.
for comp_load_order in sorted((load_order_component(component) for comp_load_order in sorted((load_order_component(component)
for component in components), for component in components),
# Test if group component exists in case key=lambda order: 'group' in order):
# above get_component call had an error.
key=lambda order:
group and group.DOMAIN in order):
load_order.update(comp_load_order) load_order.update(comp_load_order)
# Push recorder to first place in load order # Push recorder to first place in load order
if recorder.DOMAIN in load_order: if 'recorder' in load_order:
load_order.promote(recorder.DOMAIN) load_order.promote('recorder')
return load_order return load_order

View File

@ -14,7 +14,6 @@ import logging
import json import json
import enum import enum
import urllib.parse import urllib.parse
import os
import requests import requests
@ -108,20 +107,19 @@ class HomeAssistant(ha.HomeAssistant):
remote_api.host, remote_api.port, remote_api.status)) remote_api.host, remote_api.port, remote_api.status))
self.remote_api = remote_api self.remote_api = remote_api
self.local_api = local_api
self.pool = pool = ha.create_worker_pool() self.pool = pool = ha.create_worker_pool()
self.bus = EventBus(remote_api, pool) self.bus = EventBus(remote_api, pool)
self.services = ha.ServiceRegistry(self.bus, pool) self.services = ha.ServiceRegistry(self.bus, pool)
self.states = StateMachine(self.bus, self.remote_api) self.states = StateMachine(self.bus, self.remote_api)
self.components = [] self.config = ha.Config()
self.config_dir = os.path.join(os.getcwd(), 'config') self.config.api = local_api
def start(self): def start(self):
# Ensure a local API exists to connect with remote # Ensure a local API exists to connect with remote
if self.local_api is None: if self.config.api is None:
bootstrap.setup_component(self, 'http') bootstrap.setup_component(self, 'http')
bootstrap.setup_component(self, 'api') bootstrap.setup_component(self, 'api')
@ -132,10 +130,10 @@ class HomeAssistant(ha.HomeAssistant):
# Setup that events from remote_api get forwarded to local_api # Setup that events from remote_api get forwarded to local_api
# Do this after we fire START, otherwise HTTP is not started # Do this after we fire START, otherwise HTTP is not started
if not connect_remote_events(self.remote_api, self.local_api): if not connect_remote_events(self.remote_api, self.config.api):
raise ha.HomeAssistantError(( raise ha.HomeAssistantError((
'Could not setup event forwarding from api {} to ' 'Could not setup event forwarding from api {} to '
'local api {}').format(self.remote_api, self.local_api)) 'local api {}').format(self.remote_api, self.config.api))
def stop(self): def stop(self):
""" Stops Home Assistant and shuts down all threads. """ """ Stops Home Assistant and shuts down all threads. """
@ -145,7 +143,7 @@ class HomeAssistant(ha.HomeAssistant):
origin=ha.EventOrigin.remote) origin=ha.EventOrigin.remote)
# Disconnect master event forwarding # Disconnect master event forwarding
disconnect_remote_events(self.remote_api, self.local_api) disconnect_remote_events(self.remote_api, self.config.api)
# Wait till all responses to homeassistant_stop are done # Wait till all responses to homeassistant_stop are done
self.pool.block_till_done() self.pool.block_till_done()

View File

@ -36,3 +36,6 @@ pydispatcher>=2.0.5
# sensor.systemmonitor # sensor.systemmonitor
psutil>=2.2.1 psutil>=2.2.1
#pushover notifications
python-pushover>=0.2

View File

@ -1,6 +1,6 @@
# Sets up and builds python open zwave to be used with Home Assistant # Sets up and builds python open zwave to be used with Home Assistant
# Dependencies that need to be installed: # Dependencies that need to be installed:
# apt-get install cython3 libudev-dev python-sphinx python3-setuptools mercurial # apt-get install cython3 libudev-dev python-sphinx python3-setuptools
# pip3 install cython # pip3 install cython
# If current pwd is scripts, go 1 up. # If current pwd is scripts, go 1 up.
@ -8,15 +8,23 @@ if [ ${PWD##*/} == "scripts" ]; then
cd .. cd ..
fi fi
mkdir build if [ ! -d build ]; then
mkdir build
fi
cd build cd build
hg clone https://code.google.com/r/balloob-python-openzwave/ if [ -d python-openzwave ]; then
cd balloob-python-openzwave cd python-openzwave
./update.sh git pull --recurse-submodules=yes
git submodule update --init --recursive
else
git clone --recursive https://github.com/balloob/python-openzwave.git
cd python-openzwave
fi
# Fix an issue with openzwave # Fix an issue with openzwave
sed -i '253s/.*//' openzwave/cpp/src/value_classes/ValueID.h sed -i '253s/.*//' openzwave/cpp/src/value_classes/ValueID.h
./compile.sh ./compile.sh --python3
./install.sh ./install.sh --python3

View File

@ -7,14 +7,14 @@ Helper method for writing tests.
import os import os
import homeassistant as ha import homeassistant as ha
from homeassistant.helpers.device import ToggleDevice from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME
def get_test_home_assistant(): def get_test_home_assistant():
""" Returns a Home Assistant object pointing at test config dir. """ """ Returns a Home Assistant object pointing at test config dir. """
hass = ha.HomeAssistant() hass = ha.HomeAssistant()
hass.config_dir = os.path.join(os.path.dirname(__file__), "config") hass.config.config_dir = os.path.join(os.path.dirname(__file__), "config")
return hass return hass
@ -42,7 +42,7 @@ class MockModule(object):
self.setup = lambda hass, config: False if setup is None else setup self.setup = lambda hass, config: False if setup is None else setup
class MockToggleDevice(ToggleDevice): class MockToggleDevice(ToggleEntity):
""" Provides a mock toggle device. """ """ Provides a mock toggle device. """
def __init__(self, name, state): def __init__(self, name, state):
self._name = name or DEVICE_DEFAULT_NAME self._name = name or DEVICE_DEFAULT_NAME

View File

@ -63,12 +63,14 @@ class TestDemo(unittest.TestCase):
self.assertEqual( self.assertEqual(
STATE_OFF, self.hass.states.get(entity_id).state) STATE_OFF, self.hass.states.get(entity_id).state)
def test_hiding_demo_state(self): def test_if_demo_state_shows_by_default(self):
""" Test if you can hide the demo card. """ """ Test if demo state shows if we give no configuration. """
demo.setup(self.hass, {demo.DOMAIN: {'hide_demo_state': '1'}}) demo.setup(self.hass, {demo.DOMAIN: {}})
self.assertIsNone(self.hass.states.get('a.Demo_Mode'))
demo.setup(self.hass, {demo.DOMAIN: {'hide_demo_state': '0'}})
self.assertIsNotNone(self.hass.states.get('a.Demo_Mode')) self.assertIsNotNone(self.hass.states.get('a.Demo_Mode'))
def test_hiding_demo_state(self):
""" Test if you can hide the demo card. """
demo.setup(self.hass, {demo.DOMAIN: {'hide_demo_state': 1}})
self.assertIsNone(self.hass.states.get('a.Demo_Mode'))

View File

@ -32,7 +32,7 @@ class TestComponentsDeviceTracker(unittest.TestCase):
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
loader.prepare(self.hass) loader.prepare(self.hass)
self.known_dev_path = self.hass.get_config_path( self.known_dev_path = self.hass.config.path(
device_tracker.KNOWN_DEVICES_FILE) device_tracker.KNOWN_DEVICES_FILE)
def tearDown(self): # pylint: disable=invalid-name def tearDown(self): # pylint: disable=invalid-name

View File

@ -29,7 +29,7 @@ class TestLight(unittest.TestCase):
""" Stop down stuff we started. """ """ Stop down stuff we started. """
self.hass.stop() self.hass.stop()
user_light_file = self.hass.get_config_path(light.LIGHT_PROFILES_FILE) user_light_file = self.hass.config.path(light.LIGHT_PROFILES_FILE)
if os.path.isfile(user_light_file): if os.path.isfile(user_light_file):
os.remove(user_light_file) os.remove(user_light_file)
@ -218,7 +218,7 @@ class TestLight(unittest.TestCase):
platform = loader.get_component('light.test') platform = loader.get_component('light.test')
platform.init() platform.init()
user_light_file = self.hass.get_config_path(light.LIGHT_PROFILES_FILE) user_light_file = self.hass.config.path(light.LIGHT_PROFILES_FILE)
# Setup a wrong light file # Setup a wrong light file
with open(user_light_file, 'w') as user_file: with open(user_light_file, 'w') as user_file:
@ -234,7 +234,7 @@ class TestLight(unittest.TestCase):
platform = loader.get_component('light.test') platform = loader.get_component('light.test')
platform.init() platform.init()
user_light_file = self.hass.get_config_path(light.LIGHT_PROFILES_FILE) user_light_file = self.hass.config.path(light.LIGHT_PROFILES_FILE)
with open(user_light_file, 'w') as user_file: with open(user_light_file, 'w') as user_file:
user_file.write('id,x,y,brightness\n') user_file.write('id,x,y,brightness\n')

View File

@ -11,7 +11,6 @@ import datetime as dt
import ephem import ephem
import homeassistant as ha import homeassistant as ha
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
import homeassistant.components.sun as sun import homeassistant.components.sun as sun
@ -35,12 +34,9 @@ class TestSun(unittest.TestCase):
def test_setting_rising(self): def test_setting_rising(self):
""" Test retrieving sun setting and rising. """ """ Test retrieving sun setting and rising. """
# Compare it with the real data # Compare it with the real data
self.assertTrue(sun.setup( self.hass.config.latitude = '32.87336'
self.hass, self.hass.config.longitude = '117.22743'
{ha.DOMAIN: { sun.setup(self.hass, None)
CONF_LATITUDE: '32.87336',
CONF_LONGITUDE: '117.22743'
}}))
observer = ephem.Observer() observer = ephem.Observer()
observer.lat = '32.87336' # pylint: disable=assigning-non-slot observer.lat = '32.87336' # pylint: disable=assigning-non-slot
@ -74,12 +70,9 @@ class TestSun(unittest.TestCase):
def test_state_change(self): def test_state_change(self):
""" Test if the state changes at next setting/rising. """ """ Test if the state changes at next setting/rising. """
self.assertTrue(sun.setup( self.hass.config.latitude = '32.87336'
self.hass, self.hass.config.longitude = '117.22743'
{ha.DOMAIN: { sun.setup(self.hass, None)
CONF_LATITUDE: '32.87336',
CONF_LONGITUDE: '117.22743'
}}))
if sun.is_on(self.hass): if sun.is_on(self.hass):
test_state = sun.STATE_BELOW_HORIZON test_state = sun.STATE_BELOW_HORIZON
@ -96,30 +89,3 @@ class TestSun(unittest.TestCase):
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(test_state, self.hass.states.get(sun.ENTITY_ID).state) self.assertEqual(test_state, self.hass.states.get(sun.ENTITY_ID).state)
def test_setup(self):
""" Test Sun setup with empty and wrong configs. """
self.assertFalse(sun.setup(self.hass, {}))
self.assertFalse(sun.setup(self.hass, {sun.DOMAIN: {}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {CONF_LATITUDE: '32.87336'}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {CONF_LONGITUDE: '117.22743'}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {CONF_LATITUDE: 'hello'}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {CONF_LONGITUDE: 'how are you'}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {
CONF_LATITUDE: 'wrong', CONF_LONGITUDE: '117.22743'
}}))
self.assertFalse(sun.setup(
self.hass, {ha.DOMAIN: {
CONF_LATITUDE: '32.87336', CONF_LONGITUDE: 'wrong'
}}))
# Test with correct config
self.assertTrue(sun.setup(
self.hass, {ha.DOMAIN: {
CONF_LATITUDE: '32.87336', CONF_LONGITUDE: '117.22743'
}}))

View File

@ -35,10 +35,10 @@ class TestHomeAssistant(unittest.TestCase):
def test_get_config_path(self): def test_get_config_path(self):
""" Test get_config_path method. """ """ Test get_config_path method. """
self.assertEqual(os.path.join(os.getcwd(), "config"), self.assertEqual(os.path.join(os.getcwd(), "config"),
self.hass.config_dir) self.hass.config.config_dir)
self.assertEqual(os.path.join(os.getcwd(), "config", "test.conf"), self.assertEqual(os.path.join(os.getcwd(), "config", "test.conf"),
self.hass.get_config_path("test.conf")) self.hass.config.path("test.conf"))
def test_block_till_stoped(self): def test_block_till_stoped(self):
""" Test if we can block till stop service is called. """ """ Test if we can block till stop service is called. """