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/*/tellstick.py
homeassistant/components/*/vera.py
homeassistant/components/keyboard.py
homeassistant/components/switch/wemo.py
homeassistant/components/thermostat/nest.py
homeassistant/components/light/hue.py
homeassistant/components/sensor/systemmonitor.py
homeassistant/components/sensor/sabnzbd.py
homeassistant/components/notify/pushbullet.py
homeassistant/components/notify/pushover.py
homeassistant/components/media_player/cast.py
homeassistant/components/device_tracker/luci.py
homeassistant/components/device_tracker/tomato.py
homeassistant/components/device_tracker/netgear.py
homeassistant/components/device_tracker/nmap_tracker.py
homeassistant/components/light/vera.py
homeassistant/components/sensor/vera.py
homeassistant/components/switch/vera.py
[report]

3
.gitmodules vendored
View File

@ -16,3 +16,6 @@
[submodule "homeassistant/external/vera"]
path = homeassistant/external/vera
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
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/* && \
pip3 install cython && \
scripts/build_python_openzwave

View File

@ -1,8 +1,20 @@
homeassistant:
# Omitted values in this section will be auto detected using freegeoip.net
# Location required to calculate the time the sun rises and sets
latitude: 32.87336
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:
api_password: mypass
# Set to 1 to enable development mode
@ -120,4 +132,31 @@ sensor:
- type: 'memory_free'
- type: 'processor_use'
- 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 functools as ft
import requests
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED,
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
DOMAIN = "homeassistant"
@ -49,19 +52,34 @@ class HomeAssistant(object):
self.bus = EventBus(pool)
self.services = ServiceRegistry(self.bus, pool)
self.states = StateMachine(self.bus)
self.config = Config()
# List of loaded components
self.components = []
@property
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
self.local_api = None
@property
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
self.config_dir = os.path.join(os.getcwd(), 'config')
@property
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):
""" Returns path to the file within the config dir. """
return os.path.join(self.config_dir, path)
""" DEPRECATED 3/18/2015. Use hass.config.path """
_LOGGER.warning(
'hass.get_config_path is deprecated. Use hass.config.path')
return self.config.path(path)
def start(self):
""" Start home assistant. """
@ -115,6 +133,7 @@ class HomeAssistant(object):
action(now)
self.bus.listen(EVENT_TIME_CHANGED, point_in_time_listener)
return point_in_time_listener
# pylint: disable=too-many-arguments
def track_time_change(self, action,
@ -154,6 +173,7 @@ class HomeAssistant(object):
action(event.data[ATTR_NOW])
self.bus.listen(EVENT_TIME_CHANGED, time_listener)
return time_listener
def stop(self):
""" Stops Home Assistant and shuts down all threads. """
@ -457,6 +477,11 @@ class State(object):
self.last_changed = util.strip_microseconds(
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):
""" Creates a copy of itself. """
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})
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):
""" General Home Assistant exception occured. """
pass

View File

@ -20,7 +20,10 @@ import homeassistant
import homeassistant.loader as loader
import homeassistant.components as core_components
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__)
@ -28,21 +31,49 @@ ATTR_COMPONENT = "component"
def setup_component(hass, domain, config=None):
""" Setup a component for Home Assistant. """
# Check if already loaded
if domain in hass.components:
return
""" Setup a component and all its dependencies. """
if domain in hass.config.components:
return True
_ensure_loader_prepared(hass)
if config is None:
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)
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:
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
# it communicates with devices
@ -73,6 +104,8 @@ def from_config_dict(config, hass=None):
if hass is None:
hass = homeassistant.HomeAssistant()
process_ha_core_config(hass, config.get(homeassistant.DOMAIN, {}))
enable_logging(hass)
_ensure_loader_prepared(hass)
@ -97,7 +130,7 @@ def from_config_dict(config, hass=None):
# Setup the components
for domain in loader.load_order_components(components):
setup_component(hass, domain, config)
_setup_component(hass, domain, config)
return hass
@ -111,14 +144,19 @@ def from_config_file(config_path, hass=None):
if hass is None:
hass = homeassistant.HomeAssistant()
# Set config dir to directory holding config file
hass.config_dir = os.path.abspath(os.path.dirname(config_path))
# Set config dir to directory holding config file
hass.config.config_dir = os.path.abspath(os.path.dirname(config_path))
config_dict = {}
# check config file type
if os.path.splitext(config_path)[1] == '.yaml':
# Read yaml
config_dict = yaml.load(io.open(config_path, 'r'))
# If YAML file was empty
if config_dict is None:
config_dict = {}
else:
# Read config
config = configparser.ConfigParser()
@ -138,13 +176,13 @@ def enable_logging(hass):
logging.basicConfig(level=logging.INFO)
# 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)
# Check if we can write to the error log if it exists or that
# we can create files in the containing directory if not.
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_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)
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):
""" Ensure Home Assistant loader is prepared. """
if not loader.PREPARED:

View File

@ -10,7 +10,7 @@ import threading
import json
import homeassistant as ha
from homeassistant.helpers import TrackStates
from homeassistant.helpers.state import TrackStates
import homeassistant.remote as rem
from homeassistant.const import (
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):
""" 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')
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):
""" 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:
_INSTANCES[hass] = Configurator(hass)
if DOMAIN not in hass.components:
hass.components.append(DOMAIN)
if DOMAIN not in hass.config.components:
hass.config.components.append(DOMAIN)
return _INSTANCES[hass]

View File

@ -10,15 +10,14 @@ import homeassistant as ha
import homeassistant.bootstrap as bootstrap
import homeassistant.loader as loader
from homeassistant.const import (
CONF_PLATFORM, ATTR_ENTITY_PICTURE,
CONF_LATITUDE, CONF_LONGITUDE)
CONF_PLATFORM, ATTR_ENTITY_PICTURE, ATTR_ENTITY_ID)
DOMAIN = "demo"
DEPENDENCIES = []
COMPONENTS_WITH_DEMO_PLATFORM = [
'switch', 'light', 'thermostat', 'sensor', 'media_player']
'switch', 'light', 'thermostat', 'sensor', 'media_player', 'notify']
def setup(hass, config):
@ -29,16 +28,10 @@ def setup(hass, config):
config.setdefault(ha.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')
# 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)
# 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, 'bedroom', [lights[2], switches[1]])
# Setup device tracker
hass.states.set("device_tracker.Paulus", "home",
# Setup scripts
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:
"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:
"http://graph.facebook.com/anne.t.frederiksen/picture"})
hass.states.set("group.all_devices", "home",
{
"auto": True,
"entity_id": [
ATTR_ENTITY_ID: [
"device_tracker.Paulus",
"device_tracker.Anne_Therese"
]

View File

@ -153,7 +153,7 @@ def setup(hass, config):
logger.info(
"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
hass.states.track_change(

View File

@ -179,7 +179,7 @@ class DeviceTracker(object):
# Write new devices to 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:
# If file does not exist we will write the header too
@ -214,7 +214,7 @@ class DeviceTracker(object):
# pylint: disable=too-many-branches
def _read_known_devices_file(self):
""" 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
if not os.path.isfile(known_dev_path):
@ -236,7 +236,7 @@ class DeviceTracker(object):
try:
for row in csv.DictReader(inp):
device = row['device']
device = row['device'].upper()
if row['track'] == '1':
if device in self.tracked:

View File

@ -70,12 +70,17 @@ def setup(hass, config):
def new_service_listener(service, info):
""" Called when a new service is found. """
with lock:
component = SERVICE_HANDLERS.get(service)
logger.info("Found new service: %s %s", service, info)
if component and component not in hass.components:
bootstrap.setup_component(hass, component, config)
component = SERVICE_HANDLERS.get(service)
# 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, {
ATTR_SERVICE: service,

View File

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

View File

@ -1,2 +1,2 @@
""" 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="./state-card-display.html">
<link rel="import" href="../components/state-info.html">
<polymer-element name="state-card-configurator" attributes="stateObj" noscript>
<template>
<style>
.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>
<state-card-display stateObj="{{stateObj}}"></state-card-display>
<!-- pre load the image so the dialog is rendered the proper size -->
<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-thermostat.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">
<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) {
this.events = eventStore.all;
this.events = eventStore.all.toArray();
},
handleClick: function(ev) {

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

@ -6,6 +6,7 @@
<link rel="import" href="more-info-sun.html">
<link rel="import" href="more-info-configurator.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">
<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":
return "av:hearing";
case "script":
return "description";
case 'scene':
return 'social:pages';
default:
return "bookmark-outline";
}

View File

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

View File

@ -135,7 +135,7 @@ def setup(hass, config=None):
threading.Thread(target=server.start, daemon=True).start())
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

View File

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

View File

@ -1,7 +1,7 @@
""" Provides demo lights. """
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.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. """
def __init__(self, name, state, xy=None, brightness=180):
self._name = name or DEVICE_DEFAULT_NAME
@ -30,6 +30,11 @@ class DemoLight(ToggleDevice):
self._xy = xy or random.choice(LIGHT_COLORS)
self._brightness = brightness
@property
def should_poll(self):
""" No polling needed for a demo light. """
return False
@property
def name(self):
""" 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
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.components.light import (
ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION,
@ -51,7 +51,8 @@ def setup_bridge(host, hass, add_devices_callback):
try:
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
_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 """
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
from homeassistant.components.light import ATTR_BRIGHTNESS
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
@ -27,7 +27,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
add_devices_callback(lights)
class TellstickLight(ToggleDevice):
class TellstickLight(ToggleEntity):
""" Represents a tellstick light """
last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON |
tellcore_constants.TELLSTICK_TURNOFF |

View File

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

View File

@ -34,6 +34,11 @@ class DemoMediaPlayer(MediaPlayerDevice):
self.media_title = media_title
self.volume = 1.0
@property
def should_poll(self):
""" No polling needed for a demo componentn. """
return False
@property
def name(self):
""" 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):
""" 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.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):
""" Create the schedules """
if DOMAIN in hass.components:
return True
def setup_listener(schedule, event_data):
""" Creates the event listener based on event_data """
event_type = event_data['type']
@ -47,9 +44,7 @@ def setup(hass, config):
if event_type in ['time']:
component = 'scheduler.{}'.format(event_type)
elif component not in hass.components and \
not bootstrap.setup_component(hass, component, config):
elif not bootstrap.setup_component(hass, component, config):
_LOGGER.warn("Could setup event listener for %s", component)
return None
@ -74,7 +69,7 @@ def setup(hass, config):
schedule.schedule(hass)
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)
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
from homeassistant.helpers.device_component import DeviceComponent
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components import wink, zwave
DOMAIN = 'sensor'
@ -23,7 +23,7 @@ DISCOVERY_PLATFORMS = {
def setup(hass, config):
""" Track states and offer events for sensors. """
component = DeviceComponent(
component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)

View File

@ -1,25 +1,30 @@
""" Support for Wink sensors. """
from homeassistant.helpers.device import Device
from homeassistant.const import (
TEMP_CELCIUS, ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME)
from homeassistant.helpers.entity import Entity
from homeassistant.const import TEMP_CELCIUS, ATTR_BATTERY_LEVEL
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Demo sensors. """
add_devices([
DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS),
DemoSensor('Outside Humidity', 54, '%'),
DemoSensor('Outside Temperature', 15.6, TEMP_CELCIUS, 12),
DemoSensor('Outside Humidity', 54, '%', None),
])
class DemoSensor(Device):
class DemoSensor(Entity):
""" A Demo sensor. """
def __init__(self, name, state, unit_of_measurement):
def __init__(self, name, state, unit_of_measurement, battery):
self._name = name
self._state = state
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
def name(self):
@ -31,10 +36,15 @@ class DemoSensor(Device):
""" Returns the state of the device. """
return self._state
@property
def unit_of_measurement(self):
""" Unit this state is expressed in. """
return self._unit_of_measurement
@property
def state_attributes(self):
""" Returns the state attributes. """
return {
ATTR_FRIENDLY_NAME: self._name,
ATTR_UNIT_OF_MEASUREMENT: self._unit_of_measurement,
}
if self._battery:
return {
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.const import (
ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME, STATE_ON, STATE_OFF)
from homeassistant.helpers.entity import Entity
from homeassistant.const import STATE_ON, STATE_OFF
import psutil
import logging
@ -43,7 +42,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
add_devices(dev)
class SystemMonitorSensor(Device):
class SystemMonitorSensor(Entity):
""" A system monitor sensor """
def __init__(self, sensor_type, argument=''):
@ -51,7 +50,7 @@ class SystemMonitorSensor(Device):
self.argument = argument
self.type = sensor_type
self._state = None
self.unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self.update()
@property
@ -64,12 +63,8 @@ class SystemMonitorSensor(Device):
return self._state
@property
def state_attributes(self):
""" Returns the state attributes. """
return {
ATTR_FRIENDLY_NAME: self.name,
ATTR_UNIT_OF_MEASUREMENT: self.unit_of_measurement,
}
def unit_of_measurement(self):
return self._unit_of_measurement
def update(self):
if self.type == 'disk_use_percent':

View File

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

View File

@ -50,7 +50,7 @@ import logging
import time
from requests.exceptions import RequestException
from homeassistant.helpers import Device
from homeassistant.helpers.entity import Entity
from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME)
# 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))
class VeraSensor(Device):
class VeraSensor(Entity):
""" Represents a Vera Sensor """
def __init__(self, vera_device, extra_data=None):

View File

@ -4,8 +4,8 @@ import logging
# pylint: disable=no-name-in-module, import-error
import homeassistant.external.wink.pywink as pywink
from homeassistant.components.wink import WinkSensorDevice
from homeassistant.const import CONF_ACCESS_TOKEN
from homeassistant.helpers.entity import Entity
from homeassistant.const import CONF_ACCESS_TOKEN, STATE_OPEN, STATE_CLOSED
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)
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
import homeassistant.components.zwave as zwave
from homeassistant.helpers.device import Device
from homeassistant.helpers.entity import Entity
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)
@ -22,17 +22,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
value.set_change_verified(False)
if zwave.NETWORK.controller.node_id not in node.groups[1].associations:
node.groups[1].add_association(zwave.NETWORK.controller.node_id)
# if 1 in groups and (zwave.NETWORK.controller.node_id not in
# groups[1].associations):
# node.groups[1].add_association(zwave.NETWORK.controller.node_id)
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:
return [ZWaveMultilevelSensor(value)]
add_devices([ZWaveMultilevelSensor(value)])
class ZWaveSensor(Device):
class ZWaveSensor(Entity):
""" Represents a Z-Wave sensor. """
def __init__(self, sensor_value):
self._value = sensor_value
@ -76,11 +77,6 @@ class ZWaveSensor(Device):
if battery_level is not None:
attrs[ATTR_BATTERY_LEVEL] = battery_level
unit = self.unit
if unit:
attrs[ATTR_UNIT_OF_MEASUREMENT] = unit
location = self._node.location
if location:
@ -89,8 +85,7 @@ class ZWaveSensor(Device):
return attrs
@property
def unit(self):
""" Unit if sensor has one. """
def unit_of_measurement(self):
return self._value.units
def _value_changed(self, value):
@ -119,12 +114,13 @@ class ZWaveMultilevelSensor(ZWaveSensor):
if self._value.units in ('C', 'F'):
return round(value, 1)
elif isinstance(value, float):
return round(value, 2)
return value
@property
def unit(self):
""" Unit of this sensor. """
def unit_of_measurement(self):
unit = self._value.units
if unit == 'C':

View File

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

View File

@ -6,7 +6,7 @@ Component to interface with various switches that can be controlled remotely.
import logging
from datetime import timedelta
from homeassistant.helpers.device_component import DeviceComponent
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.const import (
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):
""" Track states and offer events for switches. """
component = DeviceComponent(
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, DISCOVERY_PLATFORMS,
GROUP_NAME_ALL_SWITCHES)
component.setup(config)

View File

@ -1,5 +1,5 @@
""" 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
@ -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. """
def __init__(self, name, state):
self._name = name or DEVICE_DEFAULT_NAME
self._state = state
@property
def should_poll(self):
""" No polling needed for a demo switch. """
return False
@property
def name(self):
""" 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.helpers.device import ToggleDevice
from homeassistant.helpers.entity import ToggleEntity
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)
class TellstickSwitchDevice(ToggleDevice):
class TellstickSwitchDevice(ToggleEntity):
""" represents a Tellstick switch within home assistant. """
last_sent_command_mask = (tellcore_constants.TELLSTICK_TURNON |
tellcore_constants.TELLSTICK_TURNOFF)

View File

@ -52,7 +52,7 @@ import logging
import time
from requests.exceptions import RequestException
from homeassistant.helpers import ToggleDevice
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_TRIPPED, ATTR_ARMED, ATTR_LAST_TRIP_TIME)
# 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))
class VeraSwitch(ToggleDevice):
class VeraSwitch(ToggleEntity):
""" Represents a Vera Switch """
def __init__(self, vera_device, extra_data=None):

View File

@ -1,7 +1,7 @@
""" Support for WeMo switchces. """
import logging
from homeassistant.helpers.device import ToggleDevice
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.components.switch import (
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)])
class WemoSwitch(ToggleDevice):
class WemoSwitch(ToggleEntity):
""" represents a WeMo switch within home assistant. """
def __init__(self, wemo):
self.wemo = wemo

View File

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

View File

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

View File

@ -8,10 +8,10 @@ import homeassistant.external.wink.pywink as pywink
from homeassistant import bootstrap
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 (
EVENT_PLATFORM_DISCOVERED, CONF_ACCESS_TOKEN,
STATE_OPEN, STATE_CLOSED,
ATTR_SERVICE, ATTR_DISCOVERED, ATTR_FRIENDLY_NAME)
DOMAIN = "wink"
@ -41,8 +41,7 @@ def setup(hass, config):
component = get_component(component_name)
# 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
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {
@ -53,45 +52,7 @@ def setup(hass, config):
return True
class WinkSensorDevice(Device):
""" 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):
class WinkToggleDevice(ToggleEntity):
""" represents a Wink switch within home assistant. """
def __init__(self, wink):

View File

@ -72,7 +72,7 @@ def setup(hass, config):
# Setup options
options = ZWaveOption(
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.lock()
@ -81,9 +81,9 @@ def setup(hass, config):
if use_debug:
def log_all(signal, value=None):
""" Log all the louie signals. """
""" Log all the signals. """
print("")
print("LOUIE SIGNAL *****", signal)
print("SIGNAL *****", signal)
if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED,
ZWaveNetwork.SIGNAL_VALUE_ADDED):
pprint(_obj_to_dict(value))
@ -96,8 +96,7 @@ def setup(hass, config):
for component, discovery_service, command_ids in DISCOVERY_COMPONENTS:
if value.command_class in command_ids:
# 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
hass.bus.fire(EVENT_PLATFORM_DISCOVERED, {

View File

@ -8,6 +8,9 @@ DEVICE_DEFAULT_NAME = "Unnamed Device"
# #### CONFIG ####
CONF_LATITUDE = "latitude"
CONF_LONGITUDE = "longitude"
CONF_TEMPERATURE_UNIT = "temperature_unit"
CONF_NAME = "name"
CONF_TIME_ZONE = "time_zone"
CONF_PLATFORM = "platform"
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.
"""
from datetime import datetime
from homeassistant.loader import get_component
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
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
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):
@ -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)]
# 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):
"""
Validates if all items are available in the configuration.

View File

@ -1,120 +1,10 @@
"""
homeassistant.helpers.device
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides ABC for devices in HA.
Deprecated since 3/21/2015 - please use helpers.entity
"""
import logging
from homeassistant import NoEntitySpecifiedError
# pylint: disable=unused-import
from .entity import Entity as Device, ToggleEntity as ToggleDevice # noqa
from homeassistant.const import (
ATTR_FRIENDLY_NAME, STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME)
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
logging.getLogger(__name__).warning(
'This file is deprecated. Please use helpers.entity')

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
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
import logging
# pylint: disable=unused-import
from .entity_component import EntityComponent as DeviceComponent # noqa
class DeviceComponent(object):
# pylint: disable=too-many-instance-attributes
# 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)
logging.getLogger(__name__).warning(
'This file is deprecated. Please use helpers.entity_component')

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.'))
# 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):
# 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
# custom components might only contain a platform for a component.
@ -157,28 +157,19 @@ def load_order_components(components):
"""
_check_prepared()
group = get_component('group')
recorder = get_component('recorder')
load_order = OrderedSet()
# 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
# component usually set up states that the group component requires to be
# created before it can group them.
# This does not matter in the future if we can setup groups without the
# states existing yet.
# Components that do not depend on the group usually set up states.
# Components that depend on group usually use states in their setup.
for comp_load_order in sorted((load_order_component(component)
for component in components),
# Test if group component exists in case
# above get_component call had an error.
key=lambda order:
group and group.DOMAIN in order):
key=lambda order: 'group' in order):
load_order.update(comp_load_order)
# Push recorder to first place in load order
if recorder.DOMAIN in load_order:
load_order.promote(recorder.DOMAIN)
if 'recorder' in load_order:
load_order.promote('recorder')
return load_order

View File

@ -14,7 +14,6 @@ import logging
import json
import enum
import urllib.parse
import os
import requests
@ -108,20 +107,19 @@ class HomeAssistant(ha.HomeAssistant):
remote_api.host, remote_api.port, remote_api.status))
self.remote_api = remote_api
self.local_api = local_api
self.pool = pool = ha.create_worker_pool()
self.bus = EventBus(remote_api, pool)
self.services = ha.ServiceRegistry(self.bus, pool)
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):
# 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, 'api')
@ -132,10 +130,10 @@ class HomeAssistant(ha.HomeAssistant):
# Setup that events from remote_api get forwarded to local_api
# 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((
'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):
""" Stops Home Assistant and shuts down all threads. """
@ -145,7 +143,7 @@ class HomeAssistant(ha.HomeAssistant):
origin=ha.EventOrigin.remote)
# 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
self.pool.block_till_done()

View File

@ -36,3 +36,6 @@ pydispatcher>=2.0.5
# sensor.systemmonitor
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
# 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
# If current pwd is scripts, go 1 up.
@ -8,15 +8,23 @@ if [ ${PWD##*/} == "scripts" ]; then
cd ..
fi
mkdir build
if [ ! -d build ]; then
mkdir build
fi
cd build
hg clone https://code.google.com/r/balloob-python-openzwave/
cd balloob-python-openzwave
./update.sh
if [ -d python-openzwave ]; then
cd python-openzwave
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
sed -i '253s/.*//' openzwave/cpp/src/value_classes/ValueID.h
./compile.sh
./install.sh
./compile.sh --python3
./install.sh --python3

View File

@ -7,14 +7,14 @@ Helper method for writing tests.
import os
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
def get_test_home_assistant():
""" Returns a Home Assistant object pointing at test config dir. """
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
@ -42,7 +42,7 @@ class MockModule(object):
self.setup = lambda hass, config: False if setup is None else setup
class MockToggleDevice(ToggleDevice):
class MockToggleDevice(ToggleEntity):
""" Provides a mock toggle device. """
def __init__(self, name, state):
self._name = name or DEVICE_DEFAULT_NAME

View File

@ -63,12 +63,14 @@ class TestDemo(unittest.TestCase):
self.assertEqual(
STATE_OFF, self.hass.states.get(entity_id).state)
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'))
demo.setup(self.hass, {demo.DOMAIN: {'hide_demo_state': '0'}})
def test_if_demo_state_shows_by_default(self):
""" Test if demo state shows if we give no configuration. """
demo.setup(self.hass, {demo.DOMAIN: {}})
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()
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)
def tearDown(self): # pylint: disable=invalid-name

View File

@ -29,7 +29,7 @@ class TestLight(unittest.TestCase):
""" Stop down stuff we started. """
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):
os.remove(user_light_file)
@ -218,7 +218,7 @@ class TestLight(unittest.TestCase):
platform = loader.get_component('light.test')
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
with open(user_light_file, 'w') as user_file:
@ -234,7 +234,7 @@ class TestLight(unittest.TestCase):
platform = loader.get_component('light.test')
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:
user_file.write('id,x,y,brightness\n')

View File

@ -11,7 +11,6 @@ import datetime as dt
import ephem
import homeassistant as ha
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
import homeassistant.components.sun as sun
@ -35,12 +34,9 @@ class TestSun(unittest.TestCase):
def test_setting_rising(self):
""" Test retrieving sun setting and rising. """
# Compare it with the real data
self.assertTrue(sun.setup(
self.hass,
{ha.DOMAIN: {
CONF_LATITUDE: '32.87336',
CONF_LONGITUDE: '117.22743'
}}))
self.hass.config.latitude = '32.87336'
self.hass.config.longitude = '117.22743'
sun.setup(self.hass, None)
observer = ephem.Observer()
observer.lat = '32.87336' # pylint: disable=assigning-non-slot
@ -74,12 +70,9 @@ class TestSun(unittest.TestCase):
def test_state_change(self):
""" Test if the state changes at next setting/rising. """
self.assertTrue(sun.setup(
self.hass,
{ha.DOMAIN: {
CONF_LATITUDE: '32.87336',
CONF_LONGITUDE: '117.22743'
}}))
self.hass.config.latitude = '32.87336'
self.hass.config.longitude = '117.22743'
sun.setup(self.hass, None)
if sun.is_on(self.hass):
test_state = sun.STATE_BELOW_HORIZON
@ -96,30 +89,3 @@ class TestSun(unittest.TestCase):
self.hass.pool.block_till_done()
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):
""" Test get_config_path method. """
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.hass.get_config_path("test.conf"))
self.hass.config.path("test.conf"))
def test_block_till_stoped(self):
""" Test if we can block till stop service is called. """