mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Merge remote-tracking branch 'origin/dev' into dev
Conflicts: homeassistant/components/http/frontend.py homeassistant/components/http/www_static/frontend.html
This commit is contained in:
commit
8b947e2fab
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,3 +7,6 @@
|
|||||||
[submodule "homeassistant/external/netdisco"]
|
[submodule "homeassistant/external/netdisco"]
|
||||||
path = homeassistant/external/netdisco
|
path = homeassistant/external/netdisco
|
||||||
url = https://github.com/balloob/netdisco.git
|
url = https://github.com/balloob/netdisco.git
|
||||||
|
[submodule "homeassistant/external/noop"]
|
||||||
|
path = homeassistant/external/noop
|
||||||
|
url = https://github.com/balloob/noop.git
|
||||||
|
@ -34,7 +34,6 @@ SERVICE_FLASH = 'flash'
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup example component. """
|
""" Setup example component. """
|
||||||
|
|
||||||
|
@ -426,9 +426,11 @@ class State(object):
|
|||||||
state: the state of the entity
|
state: the state of the entity
|
||||||
attributes: extra information on entity and state
|
attributes: extra information on entity and state
|
||||||
last_changed: last time the state was changed, not the attributes.
|
last_changed: last time the state was changed, not the attributes.
|
||||||
|
last_updated: last time this object was updated.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ['entity_id', 'state', 'attributes', 'last_changed']
|
__slots__ = ['entity_id', 'state', 'attributes',
|
||||||
|
'last_changed', 'last_updated']
|
||||||
|
|
||||||
def __init__(self, entity_id, state, attributes=None, last_changed=None):
|
def __init__(self, entity_id, state, attributes=None, last_changed=None):
|
||||||
if not ENTITY_ID_PATTERN.match(entity_id):
|
if not ENTITY_ID_PATTERN.match(entity_id):
|
||||||
@ -439,13 +441,14 @@ class State(object):
|
|||||||
self.entity_id = entity_id
|
self.entity_id = entity_id
|
||||||
self.state = state
|
self.state = state
|
||||||
self.attributes = attributes or {}
|
self.attributes = attributes or {}
|
||||||
|
self.last_updated = dt.datetime.now()
|
||||||
|
|
||||||
# Strip microsecond from last_changed else we cannot guarantee
|
# Strip microsecond from last_changed else we cannot guarantee
|
||||||
# state == State.from_dict(state.as_dict())
|
# state == State.from_dict(state.as_dict())
|
||||||
# This behavior occurs because to_dict uses datetime_to_str
|
# This behavior occurs because to_dict uses datetime_to_str
|
||||||
# which does not preserve microseconds
|
# which does not preserve microseconds
|
||||||
self.last_changed = util.strip_microseconds(
|
self.last_changed = util.strip_microseconds(
|
||||||
last_changed or dt.datetime.now())
|
last_changed or self.last_updated)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
""" Creates a copy of itself. """
|
""" Creates a copy of itself. """
|
||||||
@ -527,15 +530,12 @@ class StateMachine(object):
|
|||||||
def get_since(self, point_in_time):
|
def get_since(self, point_in_time):
|
||||||
"""
|
"""
|
||||||
Returns all states that have been changed since point_in_time.
|
Returns all states that have been changed since point_in_time.
|
||||||
|
|
||||||
Note: States keep track of last_changed -without- microseconds.
|
|
||||||
Therefore your point_in_time will also be stripped of microseconds.
|
|
||||||
"""
|
"""
|
||||||
point_in_time = util.strip_microseconds(point_in_time)
|
point_in_time = util.strip_microseconds(point_in_time)
|
||||||
|
|
||||||
with self._lock:
|
with self._lock:
|
||||||
return [state for state in self._states.values()
|
return [state for state in self._states.values()
|
||||||
if state.last_changed >= point_in_time]
|
if state.last_updated >= point_in_time]
|
||||||
|
|
||||||
def is_state(self, entity_id, state):
|
def is_state(self, entity_id, state):
|
||||||
""" Returns True if entity exists and is specified state. """
|
""" Returns True if entity exists and is specified state. """
|
||||||
|
@ -1,24 +1,19 @@
|
|||||||
""" Starts home assistant. """
|
""" Starts home assistant. """
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import argparse
|
import argparse
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
try:
|
|
||||||
from homeassistant import bootstrap
|
|
||||||
|
|
||||||
except ImportError:
|
def validate_python():
|
||||||
# This is to add support to load Home Assistant using
|
""" Validate we're running the right Python version. """
|
||||||
# `python3 homeassistant` instead of `python3 -m homeassistant`
|
major, minor = sys.version_info[:2]
|
||||||
|
|
||||||
# Insert the parent directory of this file into the module search path
|
if major < 3 or (major == 3 and minor < 4):
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
print("Home Assistant requires atleast Python 3.4")
|
||||||
|
sys.exit()
|
||||||
from homeassistant import bootstrap
|
|
||||||
|
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_START
|
|
||||||
from homeassistant.components import http, demo
|
|
||||||
|
|
||||||
|
|
||||||
def validate_dependencies():
|
def validate_dependencies():
|
||||||
@ -39,6 +34,34 @@ def validate_dependencies():
|
|||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_path_and_load_bootstrap():
|
||||||
|
""" Ensure sys load path is correct and load Home Assistant bootstrap. """
|
||||||
|
try:
|
||||||
|
from homeassistant import bootstrap
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
# This is to add support to load Home Assistant using
|
||||||
|
# `python3 homeassistant` instead of `python3 -m homeassistant`
|
||||||
|
|
||||||
|
# Insert the parent directory of this file into the module search path
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||||
|
|
||||||
|
from homeassistant import bootstrap
|
||||||
|
|
||||||
|
return bootstrap
|
||||||
|
|
||||||
|
|
||||||
|
def validate_git_submodules():
|
||||||
|
""" Validate the git submodules are cloned. """
|
||||||
|
try:
|
||||||
|
# pylint: disable=no-name-in-module, unused-variable
|
||||||
|
from homeassistant.external.noop import WORKING # noqa
|
||||||
|
except ImportError:
|
||||||
|
print("Repository submodules have not been initialized")
|
||||||
|
print("Please run: git submodule update --init --recursive")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
def ensure_config_path(config_dir):
|
def ensure_config_path(config_dir):
|
||||||
""" Gets the path to the configuration file.
|
""" Gets the path to the configuration file.
|
||||||
Creates one if it not exists. """
|
Creates one if it not exists. """
|
||||||
@ -65,9 +88,8 @@ def ensure_config_path(config_dir):
|
|||||||
return config_path
|
return config_path
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def get_arguments():
|
||||||
""" Starts Home Assistant. Will create demo config if no config found. """
|
""" Get parsed passed in arguments. """
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-c', '--config',
|
'-c', '--config',
|
||||||
@ -83,15 +105,26 @@ def main():
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help='Open the webinterface in a browser')
|
help='Open the webinterface in a browser')
|
||||||
|
|
||||||
args = parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
""" Starts Home Assistant. """
|
||||||
|
validate_python()
|
||||||
validate_dependencies()
|
validate_dependencies()
|
||||||
|
|
||||||
config_dir = os.path.join(os.getcwd(), args.config)
|
bootstrap = ensure_path_and_load_bootstrap()
|
||||||
|
|
||||||
|
validate_git_submodules()
|
||||||
|
|
||||||
|
args = get_arguments()
|
||||||
|
|
||||||
|
config_dir = os.path.join(os.getcwd(), args.config)
|
||||||
config_path = ensure_config_path(config_dir)
|
config_path = ensure_config_path(config_dir)
|
||||||
|
|
||||||
if args.demo_mode:
|
if args.demo_mode:
|
||||||
|
from homeassistant.components import http, demo
|
||||||
|
|
||||||
# Demo mode only requires http and demo components.
|
# Demo mode only requires http and demo components.
|
||||||
hass = bootstrap.from_config_dict({
|
hass = bootstrap.from_config_dict({
|
||||||
http.DOMAIN: {},
|
http.DOMAIN: {},
|
||||||
@ -101,7 +134,8 @@ def main():
|
|||||||
hass = bootstrap.from_config_file(config_path)
|
hass = bootstrap.from_config_file(config_path)
|
||||||
|
|
||||||
if args.open_ui:
|
if args.open_ui:
|
||||||
# pylint: disable=unused-argument
|
from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||||
|
|
||||||
def open_browser(event):
|
def open_browser(event):
|
||||||
""" Open the webinterface in a browser. """
|
""" Open the webinterface in a browser. """
|
||||||
if hass.local_api is not None:
|
if hass.local_api is not None:
|
||||||
|
@ -70,7 +70,6 @@ def turn_off(hass, entity_id=None, **service_data):
|
|||||||
hass.services.call(ha.DOMAIN, SERVICE_TURN_OFF, service_data)
|
hass.services.call(ha.DOMAIN, SERVICE_TURN_OFF, service_data)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup general services related to homeassistant. """
|
""" Setup general services related to homeassistant. """
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ def register(hass, config, action):
|
|||||||
from_state = config.get(CONF_FROM, MATCH_ALL)
|
from_state = config.get(CONF_FROM, MATCH_ALL)
|
||||||
to_state = config.get(CONF_TO, MATCH_ALL)
|
to_state = config.get(CONF_TO, MATCH_ALL)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def state_automation_listener(entity, from_s, to_s):
|
def state_automation_listener(entity, from_s, to_s):
|
||||||
""" Listens for state changes and calls action. """
|
""" Listens for state changes and calls action. """
|
||||||
action()
|
action()
|
||||||
|
@ -17,7 +17,6 @@ def register(hass, config, action):
|
|||||||
minutes = convert(config.get(CONF_MINUTES), int)
|
minutes = convert(config.get(CONF_MINUTES), int)
|
||||||
seconds = convert(config.get(CONF_SECONDS), int)
|
seconds = convert(config.get(CONF_SECONDS), int)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def time_automation_listener(now):
|
def time_automation_listener(now):
|
||||||
""" Listens for time changes and calls action. """
|
""" Listens for time changes and calls action. """
|
||||||
action()
|
action()
|
||||||
|
@ -11,7 +11,6 @@ DEPENDENCIES = []
|
|||||||
SERVICE_BROWSE_URL = "browse_url"
|
SERVICE_BROWSE_URL = "browse_url"
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Listen for browse_url events and open
|
""" Listen for browse_url events and open
|
||||||
the url in the default webbrowser. """
|
the url in the default webbrowser. """
|
||||||
|
@ -158,7 +158,6 @@ def setup(hass, config):
|
|||||||
for host in hosts:
|
for host in hosts:
|
||||||
setup_chromecast(casts, host)
|
setup_chromecast(casts, host)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def chromecast_discovered(service, info):
|
def chromecast_discovered(service, info):
|
||||||
""" Called when a Chromecast has been discovered. """
|
""" Called when a Chromecast has been discovered. """
|
||||||
logger.info("New Chromecast discovered: %s", info[0])
|
logger.info("New Chromecast discovered: %s", info[0])
|
||||||
@ -212,7 +211,7 @@ def setup(hass, config):
|
|||||||
|
|
||||||
hass.states.set(entity_id, state, state_attr)
|
hass.states.set(entity_id, state, state_attr)
|
||||||
|
|
||||||
def update_chromecast_states(time): # pylint: disable=unused-argument
|
def update_chromecast_states(time):
|
||||||
""" Updates all chromecast states. """
|
""" Updates all chromecast states. """
|
||||||
if casts:
|
if casts:
|
||||||
logger.info("Updating Chromecast status")
|
logger.info("Updating Chromecast status")
|
||||||
@ -298,7 +297,7 @@ def setup(hass, config):
|
|||||||
pychromecast.play_youtube_video(video_id, cast.host)
|
pychromecast.play_youtube_video(video_id, cast.host)
|
||||||
update_chromecast_state(entity_id, cast)
|
update_chromecast_state(entity_id, cast)
|
||||||
|
|
||||||
hass.track_time_change(update_chromecast_states)
|
hass.track_time_change(update_chromecast_states, second=[0, 15, 30, 45])
|
||||||
|
|
||||||
hass.services.register(DOMAIN, SERVICE_TURN_OFF,
|
hass.services.register(DOMAIN, SERVICE_TURN_OFF,
|
||||||
turn_off_service)
|
turn_off_service)
|
||||||
|
190
homeassistant/components/configurator.py
Normal file
190
homeassistant/components/configurator.py
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
"""
|
||||||
|
homeassistant.components.configurator
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A component to allow pieces of code to request configuration from the user.
|
||||||
|
|
||||||
|
Initiate a request by calling the `request_config` method with a callback.
|
||||||
|
This will return a request id that has to be used for future calls.
|
||||||
|
A callback has to be provided to `request_config` which will be called when
|
||||||
|
the user has submitted configuration information.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from homeassistant.helpers import generate_entity_id
|
||||||
|
from homeassistant.const import EVENT_TIME_CHANGED
|
||||||
|
|
||||||
|
DOMAIN = "configurator"
|
||||||
|
DEPENDENCIES = []
|
||||||
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||||
|
|
||||||
|
SERVICE_CONFIGURE = "configure"
|
||||||
|
|
||||||
|
STATE_CONFIGURE = "configure"
|
||||||
|
STATE_CONFIGURED = "configured"
|
||||||
|
|
||||||
|
ATTR_CONFIGURE_ID = "configure_id"
|
||||||
|
ATTR_DESCRIPTION = "description"
|
||||||
|
ATTR_DESCRIPTION_IMAGE = "description_image"
|
||||||
|
ATTR_SUBMIT_CAPTION = "submit_caption"
|
||||||
|
ATTR_FIELDS = "fields"
|
||||||
|
ATTR_ERRORS = "errors"
|
||||||
|
|
||||||
|
_REQUESTS = {}
|
||||||
|
_INSTANCES = {}
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
|
def request_config(
|
||||||
|
hass, name, callback, description=None, description_image=None,
|
||||||
|
submit_caption=None, fields=None):
|
||||||
|
""" Create a new request for config.
|
||||||
|
Will return an ID to be used for sequent calls. """
|
||||||
|
|
||||||
|
instance = _get_instance(hass)
|
||||||
|
|
||||||
|
request_id = instance.request_config(
|
||||||
|
name, callback,
|
||||||
|
description, description_image, submit_caption, fields)
|
||||||
|
|
||||||
|
_REQUESTS[request_id] = instance
|
||||||
|
|
||||||
|
return request_id
|
||||||
|
|
||||||
|
|
||||||
|
def notify_errors(request_id, error):
|
||||||
|
""" Add errors to a config request. """
|
||||||
|
try:
|
||||||
|
_REQUESTS[request_id].notify_errors(request_id, error)
|
||||||
|
except KeyError:
|
||||||
|
# If request_id does not exist
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def request_done(request_id):
|
||||||
|
""" Mark a config request as done. """
|
||||||
|
try:
|
||||||
|
_REQUESTS.pop(request_id).request_done(request_id)
|
||||||
|
except KeyError:
|
||||||
|
# If request_id does not exist
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def setup(hass, config):
|
||||||
|
""" Set up Configurator. """
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _get_instance(hass):
|
||||||
|
""" Get an instance per hass object. """
|
||||||
|
try:
|
||||||
|
return _INSTANCES[hass]
|
||||||
|
except KeyError:
|
||||||
|
_INSTANCES[hass] = Configurator(hass)
|
||||||
|
|
||||||
|
if DOMAIN not in hass.components:
|
||||||
|
hass.components.append(DOMAIN)
|
||||||
|
|
||||||
|
return _INSTANCES[hass]
|
||||||
|
|
||||||
|
|
||||||
|
class Configurator(object):
|
||||||
|
"""
|
||||||
|
Class to keep track of current configuration requests.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, hass):
|
||||||
|
self.hass = hass
|
||||||
|
self._cur_id = 0
|
||||||
|
self._requests = {}
|
||||||
|
hass.services.register(
|
||||||
|
DOMAIN, SERVICE_CONFIGURE, self.handle_service_call)
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
|
def request_config(
|
||||||
|
self, name, callback,
|
||||||
|
description, description_image, submit_caption, fields):
|
||||||
|
""" Setup a request for configuration. """
|
||||||
|
|
||||||
|
entity_id = generate_entity_id(ENTITY_ID_FORMAT, name, hass=self.hass)
|
||||||
|
|
||||||
|
if fields is None:
|
||||||
|
fields = []
|
||||||
|
|
||||||
|
request_id = self._generate_unique_id()
|
||||||
|
|
||||||
|
self._requests[request_id] = (entity_id, fields, callback)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
ATTR_CONFIGURE_ID: request_id,
|
||||||
|
ATTR_FIELDS: fields,
|
||||||
|
}
|
||||||
|
|
||||||
|
data.update({
|
||||||
|
key: value for key, value in [
|
||||||
|
(ATTR_DESCRIPTION, description),
|
||||||
|
(ATTR_DESCRIPTION_IMAGE, description_image),
|
||||||
|
(ATTR_SUBMIT_CAPTION, submit_caption),
|
||||||
|
] if value is not None
|
||||||
|
})
|
||||||
|
|
||||||
|
self.hass.states.set(entity_id, STATE_CONFIGURE, data)
|
||||||
|
|
||||||
|
return request_id
|
||||||
|
|
||||||
|
def notify_errors(self, request_id, error):
|
||||||
|
""" Update the state with errors. """
|
||||||
|
if not self._validate_request_id(request_id):
|
||||||
|
return
|
||||||
|
|
||||||
|
entity_id = self._requests[request_id][0]
|
||||||
|
|
||||||
|
state = self.hass.states.get(entity_id)
|
||||||
|
|
||||||
|
new_data = state.attributes
|
||||||
|
new_data[ATTR_ERRORS] = error
|
||||||
|
|
||||||
|
self.hass.states.set(entity_id, STATE_CONFIGURE, new_data)
|
||||||
|
|
||||||
|
def request_done(self, request_id):
|
||||||
|
""" Remove the config request. """
|
||||||
|
if not self._validate_request_id(request_id):
|
||||||
|
return
|
||||||
|
|
||||||
|
entity_id = self._requests.pop(request_id)[0]
|
||||||
|
|
||||||
|
# If we remove the state right away, it will not be included with
|
||||||
|
# the result fo the service call (current design limitation).
|
||||||
|
# Instead, we will set it to configured to give as feedback but delete
|
||||||
|
# it shortly after so that it is deleted when the client updates.
|
||||||
|
self.hass.states.set(entity_id, STATE_CONFIGURED)
|
||||||
|
|
||||||
|
def deferred_remove(event):
|
||||||
|
""" Remove the request state. """
|
||||||
|
self.hass.states.remove(entity_id)
|
||||||
|
|
||||||
|
self.hass.bus.listen_once(EVENT_TIME_CHANGED, deferred_remove)
|
||||||
|
|
||||||
|
def handle_service_call(self, call):
|
||||||
|
""" Handle a configure service call. """
|
||||||
|
request_id = call.data.get(ATTR_CONFIGURE_ID)
|
||||||
|
|
||||||
|
if not self._validate_request_id(request_id):
|
||||||
|
return
|
||||||
|
|
||||||
|
# pylint: disable=unused-variable
|
||||||
|
entity_id, fields, callback = self._requests[request_id]
|
||||||
|
|
||||||
|
# field validation goes here?
|
||||||
|
|
||||||
|
callback(call.data.get(ATTR_FIELDS, {}))
|
||||||
|
|
||||||
|
def _generate_unique_id(self):
|
||||||
|
""" Generates a unique configurator id. """
|
||||||
|
self._cur_id += 1
|
||||||
|
return "{}-{}".format(id(self), self._cur_id)
|
||||||
|
|
||||||
|
def _validate_request_id(self, request_id):
|
||||||
|
""" Validate that the request belongs to this instance. """
|
||||||
|
return request_id in self._requests
|
@ -5,6 +5,7 @@ homeassistant.components.demo
|
|||||||
Sets up a demo environment that mimics interaction with devices
|
Sets up a demo environment that mimics interaction with devices
|
||||||
"""
|
"""
|
||||||
import random
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
@ -28,6 +29,7 @@ DEPENDENCIES = []
|
|||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Setup a demo environment. """
|
""" Setup a demo environment. """
|
||||||
group = loader.get_component('group')
|
group = loader.get_component('group')
|
||||||
|
configurator = loader.get_component('configurator')
|
||||||
|
|
||||||
config.setdefault(ha.DOMAIN, {})
|
config.setdefault(ha.DOMAIN, {})
|
||||||
config.setdefault(DOMAIN, {})
|
config.setdefault(DOMAIN, {})
|
||||||
@ -170,4 +172,30 @@ def setup(hass, config):
|
|||||||
ATTR_AWAY_MODE: STATE_OFF
|
ATTR_AWAY_MODE: STATE_OFF
|
||||||
})
|
})
|
||||||
|
|
||||||
|
configurator_ids = []
|
||||||
|
|
||||||
|
def hue_configuration_callback(data):
|
||||||
|
""" Fake callback, mark config as done. """
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# First time it is called, pretend it failed.
|
||||||
|
if len(configurator_ids) == 1:
|
||||||
|
configurator.notify_errors(
|
||||||
|
configurator_ids[0],
|
||||||
|
"Failed to register, please try again.")
|
||||||
|
|
||||||
|
configurator_ids.append(0)
|
||||||
|
else:
|
||||||
|
configurator.request_done(configurator_ids[0])
|
||||||
|
|
||||||
|
request_id = configurator.request_config(
|
||||||
|
hass, "Philips Hue", hue_configuration_callback,
|
||||||
|
description=("Press the button on the bridge to register Philips Hue "
|
||||||
|
"with Home Assistant."),
|
||||||
|
description_image="/static/images/config_philips_hue.jpg",
|
||||||
|
submit_caption="I have pressed the button"
|
||||||
|
)
|
||||||
|
|
||||||
|
configurator_ids.append(request_id)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -66,7 +66,6 @@ def setup(hass, config):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def schedule_light_on_sun_rise(entity, old_state, new_state):
|
def schedule_light_on_sun_rise(entity, old_state, new_state):
|
||||||
"""The moment sun sets we want to have all the lights on.
|
"""The moment sun sets we want to have all the lights on.
|
||||||
We will schedule to have each light start after one another
|
We will schedule to have each light start after one another
|
||||||
|
@ -24,9 +24,8 @@ DEPENDENCIES = []
|
|||||||
|
|
||||||
SERVICE_DEVICE_TRACKER_RELOAD = "reload_devices_csv"
|
SERVICE_DEVICE_TRACKER_RELOAD = "reload_devices_csv"
|
||||||
|
|
||||||
GROUP_NAME_ALL_DEVICES = 'all_devices'
|
GROUP_NAME_ALL_DEVICES = 'all devices'
|
||||||
ENTITY_ID_ALL_DEVICES = group.ENTITY_ID_FORMAT.format(
|
ENTITY_ID_ALL_DEVICES = group.ENTITY_ID_FORMAT.format('all_devices')
|
||||||
GROUP_NAME_ALL_DEVICES)
|
|
||||||
|
|
||||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||||
|
|
||||||
@ -114,7 +113,6 @@ class DeviceTracker(object):
|
|||||||
dev_group = group.Group(
|
dev_group = group.Group(
|
||||||
hass, GROUP_NAME_ALL_DEVICES, user_defined=False)
|
hass, GROUP_NAME_ALL_DEVICES, user_defined=False)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def reload_known_devices_service(service):
|
def reload_known_devices_service(service):
|
||||||
""" Reload known devices file. """
|
""" Reload known devices file. """
|
||||||
self._read_known_devices_file()
|
self._read_known_devices_file()
|
||||||
@ -128,7 +126,8 @@ class DeviceTracker(object):
|
|||||||
if self.invalid_known_devices_file:
|
if self.invalid_known_devices_file:
|
||||||
return
|
return
|
||||||
|
|
||||||
hass.track_time_change(update_device_state)
|
hass.track_time_change(
|
||||||
|
update_device_state, second=[0, 12, 24, 36, 48])
|
||||||
|
|
||||||
hass.services.register(DOMAIN,
|
hass.services.register(DOMAIN,
|
||||||
SERVICE_DEVICE_TRACKER_RELOAD,
|
SERVICE_DEVICE_TRACKER_RELOAD,
|
||||||
|
@ -17,7 +17,6 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Luci scanner. """
|
""" Validates config and returns a Luci scanner. """
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
|
@ -14,7 +14,6 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Netgear scanner. """
|
""" Validates config and returns a Netgear scanner. """
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
|
@ -20,7 +20,6 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Nmap scanner. """
|
""" Validates config and returns a Nmap scanner. """
|
||||||
if not validate_config(config, {DOMAIN: [CONF_HOSTS]},
|
if not validate_config(config, {DOMAIN: [CONF_HOSTS]},
|
||||||
|
@ -20,7 +20,6 @@ CONF_HTTP_ID = "http_id"
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get_scanner(hass, config):
|
def get_scanner(hass, config):
|
||||||
""" Validates config and returns a Tomato scanner. """
|
""" Validates config and returns a Tomato scanner. """
|
||||||
if not validate_config(config,
|
if not validate_config(config,
|
||||||
|
@ -75,7 +75,6 @@ def setup(hass, config):
|
|||||||
ATTR_DISCOVERED: info
|
ATTR_DISCOVERED: info
|
||||||
})
|
})
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def start_discovery(event):
|
def start_discovery(event):
|
||||||
""" Start discovering. """
|
""" Start discovering. """
|
||||||
netdisco = DiscoveryService(SCAN_INTERVAL)
|
netdisco = DiscoveryService(SCAN_INTERVAL)
|
||||||
|
@ -163,7 +163,6 @@ class Group(object):
|
|||||||
self.hass.bus.remove_listener(
|
self.hass.bus.remove_listener(
|
||||||
ha.EVENT_STATE_CHANGED, self._update_group_state)
|
ha.EVENT_STATE_CHANGED, self._update_group_state)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def _update_group_state(self, entity_id, old_state, new_state):
|
def _update_group_state(self, entity_id, old_state, new_state):
|
||||||
""" Updates the group state based on a state change by
|
""" Updates the group state based on a state change by
|
||||||
a tracked entity. """
|
a tracked entity. """
|
||||||
|
@ -351,7 +351,6 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
""" DELETE request handler. """
|
""" DELETE request handler. """
|
||||||
self._handle_request('DELETE')
|
self._handle_request('DELETE')
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def _handle_get_root(self, path_match, data):
|
def _handle_get_root(self, path_match, data):
|
||||||
""" Renders the debug interface. """
|
""" Renders the debug interface. """
|
||||||
|
|
||||||
@ -390,17 +389,14 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
"<splash-login auth='{}'></splash-login>"
|
"<splash-login auth='{}'></splash-login>"
|
||||||
"</body></html>").format(app_url, auth))
|
"</body></html>").format(app_url, auth))
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def _handle_get_api(self, path_match, data):
|
def _handle_get_api(self, path_match, data):
|
||||||
""" Renders the debug interface. """
|
""" Renders the debug interface. """
|
||||||
self._json_message("API running.")
|
self._json_message("API running.")
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def _handle_get_api_states(self, path_match, data):
|
def _handle_get_api_states(self, path_match, data):
|
||||||
""" Returns a dict containing all entity ids and their state. """
|
""" Returns a dict containing all entity ids and their state. """
|
||||||
self._write_json(self.server.hass.states.all())
|
self._write_json(self.server.hass.states.all())
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def _handle_get_api_states_entity(self, path_match, data):
|
def _handle_get_api_states_entity(self, path_match, data):
|
||||||
""" Returns the state of a specific entity. """
|
""" Returns the state of a specific entity. """
|
||||||
entity_id = path_match.group('entity_id')
|
entity_id = path_match.group('entity_id')
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
||||||
VERSION = "1d7f65f99c286d2c897b900518ebb663"
|
VERSION = "43699d5ec727d3444985a1028d21e0d9"
|
||||||
|
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
@ -0,0 +1,28 @@
|
|||||||
|
<script src="../bower_components/moment/moment.js"></script>
|
||||||
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
|
<link rel="import" href="../components/state-info.html">
|
||||||
|
|
||||||
|
<polymer-element name="state-card-configurator" attributes="stateObj api" 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>
|
||||||
|
|
||||||
|
<!-- pre load the image so the dialog is rendered the proper size -->
|
||||||
|
<template if="{{stateObj.attributes.description_image}}">
|
||||||
|
<img hidden src="{{stateObj.attributes.description_image}}" />
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</polymer-element>
|
@ -3,6 +3,7 @@
|
|||||||
<link rel="import" href="state-card-display.html">
|
<link rel="import" href="state-card-display.html">
|
||||||
<link rel="import" href="state-card-toggle.html">
|
<link rel="import" href="state-card-toggle.html">
|
||||||
<link rel="import" href="state-card-thermostat.html">
|
<link rel="import" href="state-card-thermostat.html">
|
||||||
|
<link rel="import" href="state-card-configurator.html">
|
||||||
|
|
||||||
<polymer-element name="state-card-content" attributes="api stateObj">
|
<polymer-element name="state-card-content" attributes="api stateObj">
|
||||||
<template>
|
<template>
|
||||||
|
@ -62,6 +62,9 @@
|
|||||||
case "sensor":
|
case "sensor":
|
||||||
return "visibility";
|
return "visibility";
|
||||||
|
|
||||||
|
case "configurator":
|
||||||
|
return "settings";
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return "bookmark-outline";
|
return "bookmark-outline";
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@ Polymer({
|
|||||||
/**
|
/**
|
||||||
* Whenever the attributes change, the more info component can
|
* Whenever the attributes change, the more info component can
|
||||||
* hide or show elements. We will reposition the dialog.
|
* hide or show elements. We will reposition the dialog.
|
||||||
* DISABLED FOR NOW - BAD UX
|
|
||||||
*/
|
*/
|
||||||
reposition: function(oldVal, newVal) {
|
reposition: function(oldVal, newVal) {
|
||||||
// Only resize if already open
|
// Only resize if already open
|
||||||
|
@ -39,8 +39,8 @@
|
|||||||
<more-info-dialog id="moreInfoDialog" api={{api}}></more-info-dialog>
|
<more-info-dialog id="moreInfoDialog" api={{api}}></more-info-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
var domainsWithCard = ['thermostat'];
|
var domainsWithCard = ['thermostat', 'configurator'];
|
||||||
var domainsWithMoreInfo = ['light', 'group', 'sun'];
|
var domainsWithMoreInfo = ['light', 'group', 'sun', 'configurator'];
|
||||||
|
|
||||||
State = function(json, api) {
|
State = function(json, api) {
|
||||||
this.api = api;
|
this.api = api;
|
||||||
@ -137,6 +137,14 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
// local methods
|
// local methods
|
||||||
|
removeState: function(entityId) {
|
||||||
|
var state = this.getState(entityId);
|
||||||
|
|
||||||
|
if (state !== null) {
|
||||||
|
this.states.splice(this.states.indexOf(state), 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getState: function(entityId) {
|
getState: function(entityId) {
|
||||||
var found = this.states.filter(function(state) {
|
var found = this.states.filter(function(state) {
|
||||||
return state.entity_id == entityId;
|
return state.entity_id == entityId;
|
||||||
@ -158,6 +166,11 @@
|
|||||||
return states;
|
return states;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getEntityIDs: function() {
|
||||||
|
return this.states.map(
|
||||||
|
function(state) { return state.entity_id; });
|
||||||
|
},
|
||||||
|
|
||||||
hasService: function(domain, service) {
|
hasService: function(domain, service) {
|
||||||
var found = this.services.filter(function(serv) {
|
var found = this.services.filter(function(serv) {
|
||||||
return serv.domain == domain && serv.services.indexOf(service) !== -1;
|
return serv.domain == domain && serv.services.indexOf(service) !== -1;
|
||||||
@ -179,8 +192,8 @@
|
|||||||
this.stateUpdateTimeout = setTimeout(this.fetchStates.bind(this), 60000);
|
this.stateUpdateTimeout = setTimeout(this.fetchStates.bind(this), 60000);
|
||||||
},
|
},
|
||||||
|
|
||||||
_sortStates: function(states) {
|
_sortStates: function() {
|
||||||
states.sort(function(one, two) {
|
this.states.sort(function(one, two) {
|
||||||
if (one.entity_id > two.entity_id) {
|
if (one.entity_id > two.entity_id) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (one.entity_id < two.entity_id) {
|
} else if (one.entity_id < two.entity_id) {
|
||||||
@ -191,34 +204,62 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a new state to the state machine.
|
||||||
|
* Will resort the states after a push and fire states-updated event.
|
||||||
|
*/
|
||||||
_pushNewState: function(new_state) {
|
_pushNewState: function(new_state) {
|
||||||
var state;
|
if (this.__pushNewState(new_state)) {
|
||||||
var stateFound = false;
|
this._sortStates();
|
||||||
|
|
||||||
for(var i = 0; i < this.states.length; i++) {
|
|
||||||
if(this.states[i].entity_id == new_state.entity_id) {
|
|
||||||
state = this.states[i];
|
|
||||||
state.attributes = new_state.attributes;
|
|
||||||
state.last_changed = new_state.last_changed;
|
|
||||||
state.state = new_state.state;
|
|
||||||
|
|
||||||
stateFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!stateFound) {
|
|
||||||
this.states.push(new State(new_state, this));
|
|
||||||
this._sortStates(this.states);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fire('states-updated');
|
this.fire('states-updated');
|
||||||
},
|
},
|
||||||
|
|
||||||
_pushNewStates: function(new_states) {
|
/**
|
||||||
new_states.map(function(state) {
|
* Creates or updates a state. Returns if a new state was added.
|
||||||
this._pushNewState(state);
|
*/
|
||||||
|
__pushNewState: function(new_state) {
|
||||||
|
var curState = this.getState(new_state.entity_id);
|
||||||
|
|
||||||
|
if (curState === null) {
|
||||||
|
this.states.push(new State(new_state, this));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
curState.attributes = new_state.attributes;
|
||||||
|
curState.last_changed = new_state.last_changed;
|
||||||
|
curState.state = new_state.state;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_pushNewStates: function(newStates, removeNonPresent) {
|
||||||
|
removeNonPresent = !!removeNonPresent;
|
||||||
|
var currentEntityIds = removeNonPresent ? this.getEntityIDs() : [];
|
||||||
|
|
||||||
|
var hasNew = newStates.reduce(function(hasNew, newState) {
|
||||||
|
var isNewState = this.__pushNewState(newState);
|
||||||
|
|
||||||
|
if (isNewState) {
|
||||||
|
return true;
|
||||||
|
} else if(removeNonPresent) {
|
||||||
|
currentEntityIds.splice(currentEntityIds.indexOf(newState.entity_id), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasNew;
|
||||||
|
}.bind(this), false);
|
||||||
|
|
||||||
|
currentEntityIds.forEach(function(entityId) {
|
||||||
|
this.removeState(entityId);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
|
if (hasNew) {
|
||||||
|
this._sortStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fire('states-updated');
|
||||||
},
|
},
|
||||||
|
|
||||||
// call api methods
|
// call api methods
|
||||||
@ -238,13 +279,7 @@
|
|||||||
|
|
||||||
fetchStates: function(onSuccess, onError) {
|
fetchStates: function(onSuccess, onError) {
|
||||||
var successStatesUpdate = function(newStates) {
|
var successStatesUpdate = function(newStates) {
|
||||||
this._sortStates(newStates);
|
this._pushNewStates(newStates, true);
|
||||||
|
|
||||||
this.states = newStates.map(function(json) {
|
|
||||||
return new State(json, this);
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
this.fire('states-updated');
|
|
||||||
|
|
||||||
this._laterFetchStates();
|
this._laterFetchStates();
|
||||||
|
|
||||||
|
@ -94,15 +94,17 @@
|
|||||||
</paper-dropdown>
|
</paper-dropdown>
|
||||||
</paper-menu-button>
|
</paper-menu-button>
|
||||||
|
|
||||||
<div class="bottom fit" horizontal layout>
|
<template if="{{hasCustomGroups}}">
|
||||||
<paper-tabs id="tabsHolder" noink flex
|
<div class="bottom fit" horizontal layout>
|
||||||
selected="0" on-core-select="{{tabClicked}}">
|
<paper-tabs id="tabsHolder" noink flex
|
||||||
|
selected="0" on-core-select="{{tabClicked}}">
|
||||||
<paper-tab>ALL</paper-tab>
|
|
||||||
<paper-tab data-filter='customgroup'>GROUPS</paper-tab>
|
<paper-tab>ALL</paper-tab>
|
||||||
|
<paper-tab data-filter='customgroup'>GROUPS</paper-tab>
|
||||||
</paper-tabs>
|
|
||||||
</div>
|
</paper-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</core-toolbar>
|
</core-toolbar>
|
||||||
|
|
||||||
<state-cards
|
<state-cards
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
<link rel="import" href="../bower_components/polymer/polymer.html">
|
||||||
|
<link rel="import" href="../bower_components/paper-button/paper-button.html">
|
||||||
|
<link rel="import" href="../bower_components/paper-spinner/paper-spinner.html">
|
||||||
|
|
||||||
|
<polymer-element name="more-info-configurator" attributes="stateObj api">
|
||||||
|
<template>
|
||||||
|
<style>
|
||||||
|
p {
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p > img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.error {
|
||||||
|
color: #C62828;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.submit {
|
||||||
|
text-align: center;
|
||||||
|
height: 41px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.submit paper-spinner {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.submit span {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div layout vertical>
|
||||||
|
<template if="{{stateObj.state == 'configure'}}">
|
||||||
|
|
||||||
|
<p hidden?="{{!stateObj.attributes.description}}">
|
||||||
|
{{stateObj.attributes.description}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class='error' hidden?="{{!stateObj.attributes.errors}}">
|
||||||
|
{{stateObj.attributes.errors}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class='center' hidden?="{{!stateObj.attributes.description_image}}">
|
||||||
|
<img src='{{stateObj.attributes.description_image}}' />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class='submit'>
|
||||||
|
<paper-button raised on-click="{{submitClicked}}"
|
||||||
|
hidden?="{{action !== 'display'}}">
|
||||||
|
{{stateObj.attributes.submit_caption || "Set configuration"}}
|
||||||
|
</paper-button>
|
||||||
|
|
||||||
|
<span hidden?="{{action !== 'configuring'}}">
|
||||||
|
<paper-spinner active="true"></paper-spinner><span>Configuring…</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
Polymer({
|
||||||
|
action: "display",
|
||||||
|
|
||||||
|
submitClicked: function() {
|
||||||
|
this.action = "configuring";
|
||||||
|
var data = {
|
||||||
|
configure_id: this.stateObj.attributes.configure_id
|
||||||
|
};
|
||||||
|
|
||||||
|
this.api.call_service('configurator', 'configure', data, {
|
||||||
|
success: function() {
|
||||||
|
this.action = 'display';
|
||||||
|
this.api.fetchAll();
|
||||||
|
}.bind(this),
|
||||||
|
error: function() {
|
||||||
|
this.action = 'display';
|
||||||
|
}.bind(this)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</polymer-element>
|
@ -4,6 +4,7 @@
|
|||||||
<link rel="import" href="more-info-light.html">
|
<link rel="import" href="more-info-light.html">
|
||||||
<link rel="import" href="more-info-group.html">
|
<link rel="import" href="more-info-group.html">
|
||||||
<link rel="import" href="more-info-sun.html">
|
<link rel="import" href="more-info-sun.html">
|
||||||
|
<link rel="import" href="more-info-configurator.html">
|
||||||
|
|
||||||
<polymer-element name="more-info-content" attributes="api stateObj">
|
<polymer-element name="more-info-content" attributes="api stateObj">
|
||||||
<template>
|
<template>
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<div layout vertical>
|
<div layout vertical>
|
||||||
|
|
||||||
<template repeat="{{key in stateObj.attributes | getKeys}}">
|
<template repeat="{{key in stateObj.attributes | getKeys}}">
|
||||||
<div layout justified horizontal class='data-entry' id='rising'>
|
<div layout justified horizontal class='data-entry'>
|
||||||
<div>
|
<div>
|
||||||
{{key}}
|
{{key}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,7 +46,6 @@ def media_prev_track(hass):
|
|||||||
hass.services.call(DOMAIN, SERVICE_MEDIA_PREV_TRACK)
|
hass.services.call(DOMAIN, SERVICE_MEDIA_PREV_TRACK)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
""" Listen for keyboard events. """
|
""" Listen for keyboard events. """
|
||||||
try:
|
try:
|
||||||
|
@ -57,16 +57,15 @@ import homeassistant.util as util
|
|||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import (
|
||||||
extract_entity_ids, platform_devices_from_config)
|
generate_entity_id, extract_entity_ids, config_per_platform)
|
||||||
from homeassistant.components import group, discovery, wink
|
from homeassistant.components import group, discovery, wink
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = "light"
|
DOMAIN = "light"
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
|
||||||
GROUP_NAME_ALL_LIGHTS = 'all_lights'
|
GROUP_NAME_ALL_LIGHTS = 'all lights'
|
||||||
ENTITY_ID_ALL_LIGHTS = group.ENTITY_ID_FORMAT.format(
|
ENTITY_ID_ALL_LIGHTS = group.ENTITY_ID_FORMAT.format('all_lights')
|
||||||
GROUP_NAME_ALL_LIGHTS)
|
|
||||||
|
|
||||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||||
|
|
||||||
@ -93,6 +92,7 @@ LIGHT_PROFILES_FILE = "light_profiles.csv"
|
|||||||
# Maps discovered services to their platforms
|
# Maps discovered services to their platforms
|
||||||
DISCOVERY_PLATFORMS = {
|
DISCOVERY_PLATFORMS = {
|
||||||
wink.DISCOVER_LIGHTS: 'wink',
|
wink.DISCOVER_LIGHTS: 'wink',
|
||||||
|
discovery.services.PHILIPS_HUE: 'hue',
|
||||||
}
|
}
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -168,10 +168,33 @@ def setup(hass, config):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
lights = platform_devices_from_config(
|
# Dict to track entity_id -> lights
|
||||||
config, DOMAIN, hass, ENTITY_ID_FORMAT, _LOGGER)
|
lights = {}
|
||||||
|
|
||||||
|
# Track all lights in a group
|
||||||
|
light_group = group.Group(hass, GROUP_NAME_ALL_LIGHTS, user_defined=False)
|
||||||
|
|
||||||
|
def add_lights(new_lights):
|
||||||
|
""" Add lights to the component to track. """
|
||||||
|
for light in new_lights:
|
||||||
|
if light is not None and light not in lights.values():
|
||||||
|
light.entity_id = generate_entity_id(
|
||||||
|
ENTITY_ID_FORMAT, light.name, lights.keys())
|
||||||
|
|
||||||
|
lights[light.entity_id] = light
|
||||||
|
|
||||||
|
light.update_ha_state(hass)
|
||||||
|
|
||||||
|
light_group.update_tracked_entity_ids(lights.keys())
|
||||||
|
|
||||||
|
for p_type, p_config in config_per_platform(config, DOMAIN, _LOGGER):
|
||||||
|
platform = get_component(ENTITY_ID_FORMAT.format(p_type))
|
||||||
|
|
||||||
|
if platform is None:
|
||||||
|
_LOGGER.error("Unknown type specified: %s", p_type)
|
||||||
|
|
||||||
|
platform.setup_platform(hass, p_config, add_lights)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def update_lights_state(now):
|
def update_lights_state(now):
|
||||||
""" Update the states of all the lights. """
|
""" Update the states of all the lights. """
|
||||||
if lights:
|
if lights:
|
||||||
@ -182,28 +205,12 @@ def setup(hass, config):
|
|||||||
|
|
||||||
update_lights_state(None)
|
update_lights_state(None)
|
||||||
|
|
||||||
# Track all lights in a group
|
|
||||||
light_group = group.Group(
|
|
||||||
hass, GROUP_NAME_ALL_LIGHTS, lights.keys(), False)
|
|
||||||
|
|
||||||
def light_discovered(service, info):
|
def light_discovered(service, info):
|
||||||
""" Called when a light is discovered. """
|
""" Called when a light is discovered. """
|
||||||
platform = get_component(
|
platform = get_component(
|
||||||
"{}.{}".format(DOMAIN, DISCOVERY_PLATFORMS[service]))
|
ENTITY_ID_FORMAT.format(DISCOVERY_PLATFORMS[service]))
|
||||||
|
|
||||||
discovered = platform.devices_discovered(hass, config, info)
|
platform.setup_platform(hass, {}, add_lights, info)
|
||||||
|
|
||||||
for light in discovered:
|
|
||||||
if light is not None and light not in lights.values():
|
|
||||||
light.entity_id = util.ensure_unique_string(
|
|
||||||
ENTITY_ID_FORMAT.format(util.slugify(light.name)),
|
|
||||||
lights.keys())
|
|
||||||
|
|
||||||
lights[light.entity_id] = light
|
|
||||||
|
|
||||||
light.update_ha_state(hass)
|
|
||||||
|
|
||||||
light_group.update_tracked_entity_ids(lights.keys())
|
|
||||||
|
|
||||||
discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), light_discovered)
|
discovery.listen(hass, DISCOVERY_PLATFORMS.keys(), light_discovered)
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from homeassistant.loader import get_component
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.helpers import ToggleDevice
|
from homeassistant.helpers import ToggleDevice
|
||||||
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_HOST
|
from homeassistant.const import ATTR_FRIENDLY_NAME, CONF_HOST
|
||||||
@ -16,27 +18,59 @@ MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1)
|
|||||||
PHUE_CONFIG_FILE = "phue.conf"
|
PHUE_CONFIG_FILE = "phue.conf"
|
||||||
|
|
||||||
|
|
||||||
def get_devices(hass, config):
|
# Map ip to request id for configuring
|
||||||
|
_CONFIGURING = {}
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
""" Gets the Hue lights. """
|
""" Gets the Hue lights. """
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
try:
|
try:
|
||||||
import phue
|
# pylint: disable=unused-variable
|
||||||
|
import phue # noqa
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.exception("Error while importing dependency phue.")
|
_LOGGER.exception("Error while importing dependency phue.")
|
||||||
|
|
||||||
return []
|
return
|
||||||
|
|
||||||
host = config.get(CONF_HOST, None)
|
if discovery_info is not None:
|
||||||
|
host = urlparse(discovery_info).hostname
|
||||||
|
else:
|
||||||
|
host = config.get(CONF_HOST, None)
|
||||||
|
|
||||||
|
# Only act if we are not already configuring this host
|
||||||
|
if host in _CONFIGURING:
|
||||||
|
return
|
||||||
|
|
||||||
|
setup_bridge(host, hass, add_devices_callback)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_bridge(host, hass, add_devices_callback):
|
||||||
|
""" Setup a phue bridge based on host parameter. """
|
||||||
|
import phue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bridge = phue.Bridge(
|
bridge = phue.Bridge(
|
||||||
host, config_file_path=hass.get_config_path(PHUE_CONFIG_FILE))
|
host, config_file_path=hass.get_config_path(PHUE_CONFIG_FILE))
|
||||||
except socket.error: # Error connecting using Phue
|
except ConnectionRefusedError: # Wrong host was given
|
||||||
logger.exception((
|
_LOGGER.exception("Error connecting to the Hue bridge at %s", host)
|
||||||
"Error while connecting to the bridge. "
|
|
||||||
"Did you follow the instructions to set it up?"))
|
|
||||||
|
|
||||||
return []
|
return
|
||||||
|
|
||||||
|
except phue.PhueRegistrationException:
|
||||||
|
_LOGGER.warning("Connected to Hue at %s but not registered.", host)
|
||||||
|
|
||||||
|
request_configuration(host, hass, add_devices_callback)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# If we came here and configuring this host, mark as done
|
||||||
|
if host in _CONFIGURING:
|
||||||
|
request_id = _CONFIGURING.pop(host)
|
||||||
|
|
||||||
|
configurator = get_component('configurator')
|
||||||
|
|
||||||
|
configurator.request_done(request_id)
|
||||||
|
|
||||||
lights = {}
|
lights = {}
|
||||||
|
|
||||||
@ -47,25 +81,53 @@ def get_devices(hass, config):
|
|||||||
api = bridge.get_api()
|
api = bridge.get_api()
|
||||||
except socket.error:
|
except socket.error:
|
||||||
# socket.error when we cannot reach Hue
|
# socket.error when we cannot reach Hue
|
||||||
logger.exception("Cannot reach the bridge")
|
_LOGGER.exception("Cannot reach the bridge")
|
||||||
return
|
return
|
||||||
|
|
||||||
api_states = api.get('lights')
|
api_states = api.get('lights')
|
||||||
|
|
||||||
if not isinstance(api_states, dict):
|
if not isinstance(api_states, dict):
|
||||||
logger.error("Got unexpected result from Hue API")
|
_LOGGER.error("Got unexpected result from Hue API")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
new_lights = []
|
||||||
|
|
||||||
for light_id, info in api_states.items():
|
for light_id, info in api_states.items():
|
||||||
if light_id not in lights:
|
if light_id not in lights:
|
||||||
lights[light_id] = HueLight(int(light_id), info,
|
lights[light_id] = HueLight(int(light_id), info,
|
||||||
bridge, update_lights)
|
bridge, update_lights)
|
||||||
|
new_lights.append(lights[light_id])
|
||||||
else:
|
else:
|
||||||
lights[light_id].info = info
|
lights[light_id].info = info
|
||||||
|
|
||||||
|
if new_lights:
|
||||||
|
add_devices_callback(new_lights)
|
||||||
|
|
||||||
update_lights()
|
update_lights()
|
||||||
|
|
||||||
return list(lights.values())
|
|
||||||
|
def request_configuration(host, hass, add_devices_callback):
|
||||||
|
""" Request configuration steps from the user. """
|
||||||
|
configurator = get_component('configurator')
|
||||||
|
|
||||||
|
# We got an error if this method is called while we are configuring
|
||||||
|
if host in _CONFIGURING:
|
||||||
|
configurator.notify_errors(
|
||||||
|
_CONFIGURING[host], "Failed to register, please try again.")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def hue_configuration_callback(data):
|
||||||
|
""" Actions to do when our configuration callback is called. """
|
||||||
|
setup_bridge(host, hass, add_devices_callback)
|
||||||
|
|
||||||
|
_CONFIGURING[host] = configurator.request_config(
|
||||||
|
hass, "Philips Hue", hue_configuration_callback,
|
||||||
|
description=("Press the button on the bridge to register Philips Hue "
|
||||||
|
"with Home Assistant."),
|
||||||
|
description_image="/static/images/config_philips_hue.jpg",
|
||||||
|
submit_caption="I have pressed the button"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class HueLight(ToggleDevice):
|
class HueLight(ToggleDevice):
|
||||||
|
@ -9,31 +9,21 @@ from homeassistant.components.wink import WinkToggleDevice
|
|||||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
def get_devices(hass, config):
|
|
||||||
""" Find and return Wink lights. """
|
""" Find and return Wink lights. """
|
||||||
token = config.get(CONF_ACCESS_TOKEN)
|
token = config.get(CONF_ACCESS_TOKEN)
|
||||||
|
|
||||||
if token is None:
|
if not pywink.is_token_set() and token is None:
|
||||||
logging.getLogger(__name__).error(
|
logging.getLogger(__name__).error(
|
||||||
"Missing wink access_token - "
|
"Missing wink access_token - "
|
||||||
"get one at https://winkbearertoken.appspot.com/")
|
"get one at https://winkbearertoken.appspot.com/")
|
||||||
return []
|
return
|
||||||
|
|
||||||
pywink.set_bearer_token(token)
|
elif token is not None:
|
||||||
|
pywink.set_bearer_token(token)
|
||||||
|
|
||||||
return get_lights()
|
add_devices_callback(
|
||||||
|
WinkLight(light) for light in pywink.get_bulbs())
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def devices_discovered(hass, config, info):
|
|
||||||
""" Called when a device is discovered. """
|
|
||||||
return get_lights()
|
|
||||||
|
|
||||||
|
|
||||||
def get_lights():
|
|
||||||
""" Returns the Wink switches. """
|
|
||||||
return [WinkLight(light) for light in pywink.get_bulbs()]
|
|
||||||
|
|
||||||
|
|
||||||
class WinkLight(WinkToggleDevice):
|
class WinkLight(WinkToggleDevice):
|
||||||
|
@ -11,7 +11,6 @@ from homeassistant.const import CONF_API_KEY
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get_service(hass, config):
|
def get_service(hass, config):
|
||||||
""" Get the pushbullet notification service. """
|
""" Get the pushbullet notification service. """
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ def setup(hass, config):
|
|||||||
entities = {ENTITY_ID_FORMAT.format(util.slugify(pname)): pstring
|
entities = {ENTITY_ID_FORMAT.format(util.slugify(pname)): pstring
|
||||||
for pname, pstring in config[DOMAIN].items()}
|
for pname, pstring in config[DOMAIN].items()}
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def update_process_states(time):
|
def update_process_states(time):
|
||||||
""" Check ps for currently running processes and update states. """
|
""" Check ps for currently running processes and update states. """
|
||||||
with os.popen(PS_STRING, 'r') as psfile:
|
with os.popen(PS_STRING, 'r') as psfile:
|
||||||
|
@ -47,7 +47,6 @@ def setup(hass, config):
|
|||||||
sensors = platform_devices_from_config(
|
sensors = platform_devices_from_config(
|
||||||
config, DOMAIN, hass, ENTITY_ID_FORMAT, logger)
|
config, DOMAIN, hass, ENTITY_ID_FORMAT, logger)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def update_sensor_states(now):
|
def update_sensor_states(now):
|
||||||
""" Update states of all sensors. """
|
""" Update states of all sensors. """
|
||||||
|
@ -8,7 +8,6 @@ from homeassistant.components.wink import WinkSensorDevice
|
|||||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get_devices(hass, config):
|
def get_devices(hass, config):
|
||||||
""" Find and return Wink sensors. """
|
""" Find and return Wink sensors. """
|
||||||
token = config.get(CONF_ACCESS_TOKEN)
|
token = config.get(CONF_ACCESS_TOKEN)
|
||||||
@ -24,7 +23,6 @@ def get_devices(hass, config):
|
|||||||
return get_sensors()
|
return get_sensors()
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def devices_discovered(hass, config, info):
|
def devices_discovered(hass, config, info):
|
||||||
""" Called when a device is discovered. """
|
""" Called when a device is discovered. """
|
||||||
return get_sensors()
|
return get_sensors()
|
||||||
|
@ -78,7 +78,6 @@ def setup(hass, config):
|
|||||||
hass.services.register(
|
hass.services.register(
|
||||||
DOMAIN, SERVICE_TEST_UNKNOWN_ALARM, lambda call: unknown_alarm())
|
DOMAIN, SERVICE_TEST_UNKNOWN_ALARM, lambda call: unknown_alarm())
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def unknown_alarm_if_lights_on(entity_id, old_state, new_state):
|
def unknown_alarm_if_lights_on(entity_id, old_state, new_state):
|
||||||
""" Called when a light has been turned on. """
|
""" Called when a light has been turned on. """
|
||||||
if not device_tracker.is_on(hass):
|
if not device_tracker.is_on(hass):
|
||||||
@ -88,7 +87,6 @@ def setup(hass, config):
|
|||||||
light.ENTITY_ID_ALL_LIGHTS,
|
light.ENTITY_ID_ALL_LIGHTS,
|
||||||
unknown_alarm_if_lights_on, STATE_OFF, STATE_ON)
|
unknown_alarm_if_lights_on, STATE_OFF, STATE_ON)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def ring_known_alarm(entity_id, old_state, new_state):
|
def ring_known_alarm(entity_id, old_state, new_state):
|
||||||
""" Called when a known person comes home. """
|
""" Called when a known person comes home. """
|
||||||
if light.is_on(hass, known_light_id):
|
if light.is_on(hass, known_light_id):
|
||||||
|
@ -11,15 +11,14 @@ import homeassistant.util as util
|
|||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
STATE_ON, SERVICE_TURN_ON, SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import (
|
||||||
extract_entity_ids, platform_devices_from_config)
|
generate_entity_id, extract_entity_ids, platform_devices_from_config)
|
||||||
from homeassistant.components import group, discovery, wink
|
from homeassistant.components import group, discovery, wink
|
||||||
|
|
||||||
DOMAIN = 'switch'
|
DOMAIN = 'switch'
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
|
||||||
GROUP_NAME_ALL_SWITCHES = 'all_switches'
|
GROUP_NAME_ALL_SWITCHES = 'all switches'
|
||||||
ENTITY_ID_ALL_SWITCHES = group.ENTITY_ID_FORMAT.format(
|
ENTITY_ID_ALL_SWITCHES = group.ENTITY_ID_FORMAT.format('all_switches')
|
||||||
GROUP_NAME_ALL_SWITCHES)
|
|
||||||
|
|
||||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||||
|
|
||||||
@ -65,7 +64,6 @@ def setup(hass, config):
|
|||||||
switches = platform_devices_from_config(
|
switches = platform_devices_from_config(
|
||||||
config, DOMAIN, hass, ENTITY_ID_FORMAT, logger)
|
config, DOMAIN, hass, ENTITY_ID_FORMAT, logger)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def update_states(now):
|
def update_states(now):
|
||||||
""" Update states of all switches. """
|
""" Update states of all switches. """
|
||||||
@ -90,9 +88,8 @@ def setup(hass, config):
|
|||||||
|
|
||||||
for switch in discovered:
|
for switch in discovered:
|
||||||
if switch is not None and switch not in switches.values():
|
if switch is not None and switch not in switches.values():
|
||||||
switch.entity_id = util.ensure_unique_string(
|
switch.entity_id = generate_entity_id(
|
||||||
ENTITY_ID_FORMAT.format(util.slugify(switch.name)),
|
ENTITY_ID_FORMAT, switch.name, switches.keys())
|
||||||
switches.keys())
|
|
||||||
|
|
||||||
switches[switch.entity_id] = switch
|
switches[switch.entity_id] = switch
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ except ImportError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get_devices(hass, config):
|
def get_devices(hass, config):
|
||||||
""" Find and return Tellstick switches. """
|
""" Find and return Tellstick switches. """
|
||||||
try:
|
try:
|
||||||
@ -54,12 +53,10 @@ class TellstickSwitch(ToggleDevice):
|
|||||||
|
|
||||||
return last_command == tc_constants.TELLSTICK_TURNON
|
return last_command == tc_constants.TELLSTICK_TURNON
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def turn_on(self, **kwargs):
|
def turn_on(self, **kwargs):
|
||||||
""" Turns the switch on. """
|
""" Turns the switch on. """
|
||||||
self.tellstick.turn_on()
|
self.tellstick.turn_on()
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def turn_off(self, **kwargs):
|
def turn_off(self, **kwargs):
|
||||||
""" Turns the switch off. """
|
""" Turns the switch off. """
|
||||||
self.tellstick.turn_off()
|
self.tellstick.turn_off()
|
||||||
|
@ -7,7 +7,6 @@ from homeassistant.components.switch import (
|
|||||||
ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH)
|
ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get_devices(hass, config):
|
def get_devices(hass, config):
|
||||||
""" Find and return WeMo switches. """
|
""" Find and return WeMo switches. """
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ from homeassistant.components.wink import WinkToggleDevice
|
|||||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get_devices(hass, config):
|
def get_devices(hass, config):
|
||||||
""" Find and return Wink switches. """
|
""" Find and return Wink switches. """
|
||||||
token = config.get(CONF_ACCESS_TOKEN)
|
token = config.get(CONF_ACCESS_TOKEN)
|
||||||
@ -24,7 +23,6 @@ def get_devices(hass, config):
|
|||||||
return get_switches()
|
return get_switches()
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def devices_discovered(hass, config, info):
|
def devices_discovered(hass, config, info):
|
||||||
""" Called when a device is discovered. """
|
""" Called when a device is discovered. """
|
||||||
return get_switches()
|
return get_switches()
|
||||||
|
@ -129,7 +129,6 @@ def setup(hass, config):
|
|||||||
sensor.has_value(datatype):
|
sensor.has_value(datatype):
|
||||||
update_sensor_value_state(sensor_name, sensor.value(datatype))
|
update_sensor_value_state(sensor_name, sensor.value(datatype))
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def update_sensors_state(time):
|
def update_sensors_state(time):
|
||||||
""" Update the state of all sensors """
|
""" Update the state of all sensors """
|
||||||
for sensor in sensors:
|
for sensor in sensors:
|
||||||
|
@ -67,7 +67,6 @@ def setup(hass, config):
|
|||||||
if not thermostats:
|
if not thermostats:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
@util.Throttle(MIN_TIME_BETWEEN_SCANS)
|
||||||
def update_state(now):
|
def update_state(now):
|
||||||
""" Update thermostat state. """
|
""" Update thermostat state. """
|
||||||
|
@ -7,7 +7,6 @@ from homeassistant.components.thermostat import ThermostatDevice
|
|||||||
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS)
|
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def get_devices(hass, config):
|
def get_devices(hass, config):
|
||||||
""" Gets Nest thermostats. """
|
""" Gets Nest thermostats. """
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
1
homeassistant/external/noop
vendored
Submodule
1
homeassistant/external/noop
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 45fae73c1f44342010fa07f3ed8909bf2819a508
|
5
homeassistant/external/wink/pywink.py
vendored
5
homeassistant/external/wink/pywink.py
vendored
@ -389,6 +389,11 @@ def get_switches():
|
|||||||
def get_sensors():
|
def get_sensors():
|
||||||
return get_devices('sensor_pod_id', wink_sensor_pod)
|
return get_devices('sensor_pod_id', wink_sensor_pod)
|
||||||
|
|
||||||
|
def is_token_set():
|
||||||
|
""" Returns if an auth token has been set. """
|
||||||
|
return bool(headers)
|
||||||
|
|
||||||
|
|
||||||
def set_bearer_token(token):
|
def set_bearer_token(token):
|
||||||
global headers
|
global headers
|
||||||
|
|
||||||
|
@ -12,6 +12,18 @@ from homeassistant.const import (
|
|||||||
from homeassistant.util import ensure_unique_string, slugify
|
from homeassistant.util import ensure_unique_string, slugify
|
||||||
|
|
||||||
|
|
||||||
|
def generate_entity_id(entity_id_format, name, current_ids=None, hass=None):
|
||||||
|
""" Generate a unique entity ID based on given entity IDs or used ids. """
|
||||||
|
if current_ids is None:
|
||||||
|
if hass is None:
|
||||||
|
raise RuntimeError("Missing required parameter currentids or hass")
|
||||||
|
|
||||||
|
current_ids = hass.states.entity_ids()
|
||||||
|
|
||||||
|
return ensure_unique_string(
|
||||||
|
entity_id_format.format(slugify(name)), current_ids)
|
||||||
|
|
||||||
|
|
||||||
def extract_entity_ids(hass, service):
|
def extract_entity_ids(hass, service):
|
||||||
"""
|
"""
|
||||||
Helper method to extract a list of entity ids from a service call.
|
Helper method to extract a list of entity ids from a service call.
|
||||||
@ -160,9 +172,8 @@ def platform_devices_from_config(config, domain, hass,
|
|||||||
no_name_count += 1
|
no_name_count += 1
|
||||||
name = "{} {}".format(domain, no_name_count)
|
name = "{} {}".format(domain, no_name_count)
|
||||||
|
|
||||||
entity_id = ensure_unique_string(
|
entity_id = generate_entity_id(
|
||||||
entity_id_format.format(slugify(name)),
|
entity_id_format, name, device_dict.keys())
|
||||||
device_dict.keys())
|
|
||||||
|
|
||||||
device.entity_id = entity_id
|
device.entity_id = entity_id
|
||||||
device_dict[entity_id] = device
|
device_dict[entity_id] = device
|
||||||
|
4
pylintrc
4
pylintrc
@ -8,12 +8,14 @@ reports=no
|
|||||||
# cyclic-import - doesn't test if both import on load
|
# cyclic-import - doesn't test if both import on load
|
||||||
# abstract-class-little-used - Prevents from setting right foundation
|
# abstract-class-little-used - Prevents from setting right foundation
|
||||||
# abstract-class-not-used - is flaky, should not show up but does
|
# abstract-class-not-used - is flaky, should not show up but does
|
||||||
|
# unused-argument - generic callbacks and setup methods create a lot of warnings
|
||||||
disable=
|
disable=
|
||||||
locally-disabled,
|
locally-disabled,
|
||||||
duplicate-code,
|
duplicate-code,
|
||||||
cyclic-import,
|
cyclic-import,
|
||||||
abstract-class-little-used,
|
abstract-class-little-used,
|
||||||
abstract-class-not-used
|
abstract-class-not-used,
|
||||||
|
unused-argument
|
||||||
|
|
||||||
[EXCEPTIONS]
|
[EXCEPTIONS]
|
||||||
overgeneral-exceptions=Exception,HomeAssistantError
|
overgeneral-exceptions=Exception,HomeAssistantError
|
||||||
|
@ -5,3 +5,4 @@ fi
|
|||||||
|
|
||||||
git pull --recurse-submodules=yes
|
git pull --recurse-submodules=yes
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
|
python3 -m pip install -r requirements.txt
|
||||||
|
@ -24,6 +24,11 @@ def init(empty=False):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_lights(hass, config):
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
""" Returns mock devices. """
|
""" Returns mock devices. """
|
||||||
|
add_devices_callback(DEVICES)
|
||||||
|
|
||||||
|
|
||||||
|
def get_lights():
|
||||||
|
""" Helper method to get current light objects. """
|
||||||
return DEVICES
|
return DEVICES
|
||||||
|
120
tests/test_component_configurator.py
Normal file
120
tests/test_component_configurator.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
"""
|
||||||
|
tests.test_component_configurator
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Tests Configurator component.
|
||||||
|
"""
|
||||||
|
# pylint: disable=too-many-public-methods,protected-access
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import homeassistant as ha
|
||||||
|
import homeassistant.components.configurator as configurator
|
||||||
|
from homeassistant.const import EVENT_TIME_CHANGED
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfigurator(unittest.TestCase):
|
||||||
|
""" Test the chromecast module. """
|
||||||
|
|
||||||
|
def setUp(self): # pylint: disable=invalid-name
|
||||||
|
self.hass = ha.HomeAssistant()
|
||||||
|
|
||||||
|
def tearDown(self): # pylint: disable=invalid-name
|
||||||
|
""" Stop down stuff we started. """
|
||||||
|
self.hass.stop()
|
||||||
|
|
||||||
|
def test_request_least_info(self):
|
||||||
|
""" Test request config with least amount of data. """
|
||||||
|
|
||||||
|
request_id = configurator.request_config(
|
||||||
|
self.hass, "Test Request", lambda _: None)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
1, len(self.hass.services.services.get(configurator.DOMAIN, [])),
|
||||||
|
"No new service registered")
|
||||||
|
|
||||||
|
states = self.hass.states.all()
|
||||||
|
|
||||||
|
self.assertEqual(1, len(states), "Expected a new state registered")
|
||||||
|
|
||||||
|
state = states[0]
|
||||||
|
|
||||||
|
self.assertEqual(configurator.STATE_CONFIGURE, state.state)
|
||||||
|
self.assertEqual(
|
||||||
|
request_id, state.attributes.get(configurator.ATTR_CONFIGURE_ID))
|
||||||
|
|
||||||
|
def test_request_all_info(self):
|
||||||
|
""" Test request config with all possible info. """
|
||||||
|
|
||||||
|
values = [
|
||||||
|
"config_description", "config image url",
|
||||||
|
"config submit caption", []]
|
||||||
|
|
||||||
|
keys = [
|
||||||
|
configurator.ATTR_DESCRIPTION, configurator.ATTR_DESCRIPTION_IMAGE,
|
||||||
|
configurator.ATTR_SUBMIT_CAPTION, configurator.ATTR_FIELDS]
|
||||||
|
|
||||||
|
exp_attr = dict(zip(keys, values))
|
||||||
|
|
||||||
|
exp_attr[configurator.ATTR_CONFIGURE_ID] = configurator.request_config(
|
||||||
|
self.hass, "Test Request", lambda _: None,
|
||||||
|
*values)
|
||||||
|
|
||||||
|
states = self.hass.states.all()
|
||||||
|
|
||||||
|
self.assertEqual(1, len(states))
|
||||||
|
|
||||||
|
state = states[0]
|
||||||
|
|
||||||
|
self.assertEqual(configurator.STATE_CONFIGURE, state.state)
|
||||||
|
self.assertEqual(exp_attr, state.attributes)
|
||||||
|
|
||||||
|
def test_callback_called_on_configure(self):
|
||||||
|
""" Test if our callback gets called when configure service called. """
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
request_id = configurator.request_config(
|
||||||
|
self.hass, "Test Request", lambda _: calls.append(1))
|
||||||
|
|
||||||
|
self.hass.services.call(
|
||||||
|
configurator.DOMAIN, configurator.SERVICE_CONFIGURE,
|
||||||
|
{configurator.ATTR_CONFIGURE_ID: request_id})
|
||||||
|
|
||||||
|
self.hass.pool.block_till_done()
|
||||||
|
|
||||||
|
self.assertEqual(1, len(calls), "Callback not called")
|
||||||
|
|
||||||
|
def test_state_change_on_notify_errors(self):
|
||||||
|
""" Test state change on notify errors. """
|
||||||
|
request_id = configurator.request_config(
|
||||||
|
self.hass, "Test Request", lambda _: None)
|
||||||
|
|
||||||
|
error = "Oh no bad bad bad"
|
||||||
|
|
||||||
|
configurator.notify_errors(request_id, error)
|
||||||
|
|
||||||
|
state = self.hass.states.all()[0]
|
||||||
|
|
||||||
|
self.assertEqual(error, state.attributes.get(configurator.ATTR_ERRORS))
|
||||||
|
|
||||||
|
def test_notify_errors_fail_silently_on_bad_request_id(self):
|
||||||
|
""" Test if notify errors fails silently with a bad request id. """
|
||||||
|
configurator.notify_errors(2015, "Try this error")
|
||||||
|
|
||||||
|
def test_request_done_works(self):
|
||||||
|
""" Test if calling request done works. """
|
||||||
|
request_id = configurator.request_config(
|
||||||
|
self.hass, "Test Request", lambda _: None)
|
||||||
|
|
||||||
|
configurator.request_done(request_id)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(self.hass.states.all()))
|
||||||
|
|
||||||
|
self.hass.bus.fire(EVENT_TIME_CHANGED)
|
||||||
|
|
||||||
|
self.hass.pool.block_till_done()
|
||||||
|
|
||||||
|
self.assertEqual(0, len(self.hass.states.all()))
|
||||||
|
|
||||||
|
def test_request_done_fail_silently_on_bad_request_id(self):
|
||||||
|
""" Test that request_done fails silently with a bad request id. """
|
||||||
|
configurator.request_done(2016)
|
@ -117,9 +117,15 @@ class TestComponentsDeviceTracker(unittest.TestCase):
|
|||||||
dev3 = device_tracker.ENTITY_ID_FORMAT.format('DEV3')
|
dev3 = device_tracker.ENTITY_ID_FORMAT.format('DEV3')
|
||||||
|
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
nowAlmostMinGone = (now + device_tracker.TIME_DEVICE_NOT_FOUND -
|
|
||||||
timedelta(seconds=1))
|
# Device scanner scans every 12 seconds. We need to sync our times to
|
||||||
nowMinGone = nowAlmostMinGone + timedelta(seconds=2)
|
# be every 12 seconds or else the time_changed event will be ignored.
|
||||||
|
nowAlmostMinimumGone = now + device_tracker.TIME_DEVICE_NOT_FOUND
|
||||||
|
nowAlmostMinimumGone -= timedelta(
|
||||||
|
seconds=12+(nowAlmostMinimumGone.second % 12))
|
||||||
|
|
||||||
|
nowMinimumGone = now + device_tracker.TIME_DEVICE_NOT_FOUND
|
||||||
|
nowMinimumGone += timedelta(seconds=12-(nowMinimumGone.second % 12))
|
||||||
|
|
||||||
# Test initial is correct
|
# Test initial is correct
|
||||||
self.assertTrue(device_tracker.is_on(self.hass))
|
self.assertTrue(device_tracker.is_on(self.hass))
|
||||||
@ -145,10 +151,10 @@ class TestComponentsDeviceTracker(unittest.TestCase):
|
|||||||
fil.write('dev2,Device 2,1,http://example.com/picture.jpg\n')
|
fil.write('dev2,Device 2,1,http://example.com/picture.jpg\n')
|
||||||
fil.write('dev3,DEV3,1,\n')
|
fil.write('dev3,DEV3,1,\n')
|
||||||
|
|
||||||
# reload dev file
|
|
||||||
scanner.come_home('dev1')
|
scanner.come_home('dev1')
|
||||||
scanner.leave_home('dev2')
|
scanner.leave_home('dev2')
|
||||||
|
|
||||||
|
# reload dev file
|
||||||
self.hass.services.call(
|
self.hass.services.call(
|
||||||
device_tracker.DOMAIN,
|
device_tracker.DOMAIN,
|
||||||
device_tracker.SERVICE_DEVICE_TRACKER_RELOAD)
|
device_tracker.SERVICE_DEVICE_TRACKER_RELOAD)
|
||||||
@ -168,7 +174,7 @@ class TestComponentsDeviceTracker(unittest.TestCase):
|
|||||||
|
|
||||||
# Test if device leaves what happens, test the time span
|
# Test if device leaves what happens, test the time span
|
||||||
self.hass.bus.fire(
|
self.hass.bus.fire(
|
||||||
ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: nowAlmostMinGone})
|
ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: nowAlmostMinimumGone})
|
||||||
|
|
||||||
self.hass.pool.block_till_done()
|
self.hass.pool.block_till_done()
|
||||||
|
|
||||||
@ -179,7 +185,8 @@ class TestComponentsDeviceTracker(unittest.TestCase):
|
|||||||
self.assertTrue(device_tracker.is_on(self.hass, dev3))
|
self.assertTrue(device_tracker.is_on(self.hass, dev3))
|
||||||
|
|
||||||
# Now test if gone for longer then error margin
|
# Now test if gone for longer then error margin
|
||||||
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: nowMinGone})
|
self.hass.bus.fire(
|
||||||
|
ha.EVENT_TIME_CHANGED, {ha.ATTR_NOW: nowMinimumGone})
|
||||||
|
|
||||||
self.hass.pool.block_till_done()
|
self.hass.pool.block_till_done()
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ class TestHTTP(unittest.TestCase):
|
|||||||
""" Test if the API allows us to fire an event. """
|
""" Test if the API allows us to fire an event. """
|
||||||
test_value = []
|
test_value = []
|
||||||
|
|
||||||
def listener(event): # pylint: disable=unused-argument
|
def listener(event):
|
||||||
""" Helper method that will verify our event got called. """
|
""" Helper method that will verify our event got called. """
|
||||||
test_value.append(1)
|
test_value.append(1)
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ class TestHTTP(unittest.TestCase):
|
|||||||
""" Test if the API allows us to fire an event. """
|
""" Test if the API allows us to fire an event. """
|
||||||
test_value = []
|
test_value = []
|
||||||
|
|
||||||
def listener(event): # pylint: disable=unused-argument
|
def listener(event):
|
||||||
""" Helper method that will verify that our event got called and
|
""" Helper method that will verify that our event got called and
|
||||||
that test if our data came through. """
|
that test if our data came through. """
|
||||||
if "test" in event.data:
|
if "test" in event.data:
|
||||||
@ -225,7 +225,7 @@ class TestHTTP(unittest.TestCase):
|
|||||||
""" Test if the API allows us to fire an event. """
|
""" Test if the API allows us to fire an event. """
|
||||||
test_value = []
|
test_value = []
|
||||||
|
|
||||||
def listener(event): # pylint: disable=unused-argument
|
def listener(event):
|
||||||
""" Helper method that will verify our event got called. """
|
""" Helper method that will verify our event got called. """
|
||||||
test_value.append(1)
|
test_value.append(1)
|
||||||
|
|
||||||
@ -281,7 +281,7 @@ class TestHTTP(unittest.TestCase):
|
|||||||
""" Test if the API allows us to call a service. """
|
""" Test if the API allows us to call a service. """
|
||||||
test_value = []
|
test_value = []
|
||||||
|
|
||||||
def listener(service_call): # pylint: disable=unused-argument
|
def listener(service_call):
|
||||||
""" Helper method that will verify that our service got called. """
|
""" Helper method that will verify that our service got called. """
|
||||||
test_value.append(1)
|
test_value.append(1)
|
||||||
|
|
||||||
@ -300,7 +300,7 @@ class TestHTTP(unittest.TestCase):
|
|||||||
""" Test if the API allows us to call a service. """
|
""" Test if the API allows us to call a service. """
|
||||||
test_value = []
|
test_value = []
|
||||||
|
|
||||||
def listener(service_call): # pylint: disable=unused-argument
|
def listener(service_call):
|
||||||
""" Helper method that will verify that our service got called and
|
""" Helper method that will verify that our service got called and
|
||||||
that test if our data came through. """
|
that test if our data came through. """
|
||||||
if "test" in service_call.data:
|
if "test" in service_call.data:
|
||||||
|
@ -8,7 +8,6 @@ Tests switch component.
|
|||||||
import unittest
|
import unittest
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import homeassistant as ha
|
|
||||||
import homeassistant.loader as loader
|
import homeassistant.loader as loader
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@ -104,7 +103,7 @@ class TestLight(unittest.TestCase):
|
|||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
light.setup(self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}))
|
light.setup(self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}))
|
||||||
|
|
||||||
dev1, dev2, dev3 = platform.get_lights(None, None)
|
dev1, dev2, dev3 = platform.get_lights()
|
||||||
|
|
||||||
# Test init
|
# Test init
|
||||||
self.assertTrue(light.is_on(self.hass, dev1.entity_id))
|
self.assertTrue(light.is_on(self.hass, dev1.entity_id))
|
||||||
@ -214,7 +213,7 @@ class TestLight(unittest.TestCase):
|
|||||||
light.ATTR_XY_COLOR: [prof_x, prof_y]},
|
light.ATTR_XY_COLOR: [prof_x, prof_y]},
|
||||||
data)
|
data)
|
||||||
|
|
||||||
def test_light_profiles(self):
|
def test_broken_light_profiles(self):
|
||||||
""" Test light profiles. """
|
""" Test light profiles. """
|
||||||
platform = loader.get_component('light.test')
|
platform = loader.get_component('light.test')
|
||||||
platform.init()
|
platform.init()
|
||||||
@ -230,8 +229,12 @@ class TestLight(unittest.TestCase):
|
|||||||
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
||||||
))
|
))
|
||||||
|
|
||||||
# Clean up broken file
|
def test_light_profiles(self):
|
||||||
os.remove(user_light_file)
|
""" Test light profiles. """
|
||||||
|
platform = loader.get_component('light.test')
|
||||||
|
platform.init()
|
||||||
|
|
||||||
|
user_light_file = self.hass.get_config_path(light.LIGHT_PROFILES_FILE)
|
||||||
|
|
||||||
with open(user_light_file, 'w') as user_file:
|
with open(user_light_file, 'w') as user_file:
|
||||||
user_file.write('id,x,y,brightness\n')
|
user_file.write('id,x,y,brightness\n')
|
||||||
@ -241,7 +244,7 @@ class TestLight(unittest.TestCase):
|
|||||||
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
||||||
))
|
))
|
||||||
|
|
||||||
dev1, dev2, dev3 = platform.get_lights(None, None)
|
dev1, dev2, dev3 = platform.get_lights()
|
||||||
|
|
||||||
light.turn_on(self.hass, dev1.entity_id, profile='test')
|
light.turn_on(self.hass, dev1.entity_id, profile='test')
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ class TestRemoteMethods(unittest.TestCase):
|
|||||||
""" Test Python API fire_event. """
|
""" Test Python API fire_event. """
|
||||||
test_value = []
|
test_value = []
|
||||||
|
|
||||||
def listener(event): # pylint: disable=unused-argument
|
def listener(event):
|
||||||
""" Helper method that will verify our event got called. """
|
""" Helper method that will verify our event got called. """
|
||||||
test_value.append(1)
|
test_value.append(1)
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ class TestRemoteMethods(unittest.TestCase):
|
|||||||
""" Test Python API services.call. """
|
""" Test Python API services.call. """
|
||||||
test_value = []
|
test_value = []
|
||||||
|
|
||||||
def listener(service_call): # pylint: disable=unused-argument
|
def listener(service_call):
|
||||||
""" Helper method that will verify that our service got called. """
|
""" Helper method that will verify that our service got called. """
|
||||||
test_value.append(1)
|
test_value.append(1)
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ class TestRemoteClasses(unittest.TestCase):
|
|||||||
""" Test if events fired from the eventbus get fired. """
|
""" Test if events fired from the eventbus get fired. """
|
||||||
test_value = []
|
test_value = []
|
||||||
|
|
||||||
def listener(event): # pylint: disable=unused-argument
|
def listener(event):
|
||||||
""" Helper method that will verify our event got called. """
|
""" Helper method that will verify our event got called. """
|
||||||
test_value.append(1)
|
test_value.append(1)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user