Merge pull request #51 from balloob/dev

Merge dev into master
This commit is contained in:
Theodor Lindquist 2015-03-08 08:56:01 +01:00
commit 851bff1e12
47 changed files with 1070 additions and 822 deletions

40
.coveragerc Normal file
View File

@ -0,0 +1,40 @@
[run]
source = homeassistant
omit =
homeassistant/external/*
# omit pieces of code that rely on external devices being present
homeassistant/components/wink.py
homeassistant/components/*/wink.py
homeassistant/components/zwave.py
homeassistant/components/*/zwave.py
homeassistant/components/*/tellstick.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/notify/pushbullet.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
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain about missing debug-only code:
def __repr__
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError

View File

@ -7,6 +7,6 @@ install:
script: script:
- flake8 homeassistant --exclude bower_components,external - flake8 homeassistant --exclude bower_components,external
- pylint homeassistant - pylint homeassistant
- coverage run --source=homeassistant --omit "homeassistant/external/*" -m unittest discover tests - coverage run -m unittest discover tests
after_success: after_success:
- coveralls - coveralls

View File

@ -102,3 +102,21 @@ automation 2:
execute_service: notify.notify execute_service: notify.notify
service_data: {"message":"It's 4, time for beer!"} service_data: {"message":"It's 4, time for beer!"}
sensor:
platform: systemmonitor
resources:
- type: 'disk_use_percent'
arg: '/'
- type: 'disk_use_percent'
arg: '/home'
- type: 'disk_use'
arg: '/home'
- type: 'disk_free'
arg: '/'
- type: 'memory_use_percent'
- type: 'memory_use'
- type: 'memory_free'
- type: 'processor_use'
- type: 'process'
arg: 'octave-cli'

View File

@ -1,339 +0,0 @@
"""
homeassistant.components.chromecast
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to interact with Chromecasts.
"""
import logging
try:
import pychromecast
except ImportError:
# Ignore, we will raise appropriate error later
pass
from homeassistant.loader import get_component
import homeassistant.util as util
from homeassistant.helpers import extract_entity_ids
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, SERVICE_TURN_OFF, SERVICE_VOLUME_UP,
SERVICE_VOLUME_DOWN, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY,
SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREV_TRACK)
DOMAIN = 'chromecast'
DEPENDENCIES = []
SERVICE_YOUTUBE_VIDEO = 'play_youtube_video'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
STATE_NO_APP = 'idle'
ATTR_STATE = 'state'
ATTR_OPTIONS = 'options'
ATTR_MEDIA_STATE = 'media_state'
ATTR_MEDIA_CONTENT_ID = 'media_content_id'
ATTR_MEDIA_TITLE = 'media_title'
ATTR_MEDIA_ARTIST = 'media_artist'
ATTR_MEDIA_ALBUM = 'media_album'
ATTR_MEDIA_IMAGE_URL = 'media_image_url'
ATTR_MEDIA_VOLUME = 'media_volume'
ATTR_MEDIA_DURATION = 'media_duration'
MEDIA_STATE_UNKNOWN = 'unknown'
MEDIA_STATE_PLAYING = 'playing'
MEDIA_STATE_STOPPED = 'stopped'
def is_on(hass, entity_id=None):
""" Returns true if specified ChromeCast entity_id is on.
Will check all chromecasts if no entity_id specified. """
entity_ids = [entity_id] if entity_id else hass.states.entity_ids(DOMAIN)
return any(not hass.states.is_state(entity_id, STATE_NO_APP)
for entity_id in entity_ids)
def turn_off(hass, entity_id=None):
""" Will turn off specified Chromecast or all. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
def volume_up(hass, entity_id=None):
""" Send the chromecast the command for volume up. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_VOLUME_UP, data)
def volume_down(hass, entity_id=None):
""" Send the chromecast the command for volume down. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_VOLUME_DOWN, data)
def media_play_pause(hass, entity_id=None):
""" Send the chromecast the command for play/pause. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE, data)
def media_play(hass, entity_id=None):
""" Send the chromecast the command for play/pause. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PLAY, data)
def media_pause(hass, entity_id=None):
""" Send the chromecast the command for play/pause. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PAUSE, data)
def media_next_track(hass, entity_id=None):
""" Send the chromecast the command for next track. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_NEXT_TRACK, data)
def media_prev_track(hass, entity_id=None):
""" Send the chromecast the command for prev track. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PREV_TRACK, data)
def setup_chromecast(casts, host):
""" Tries to convert host to Chromecast object and set it up. """
# Check if already setup
if any(cast.host == host for cast in casts.values()):
return
try:
cast = pychromecast.PyChromecast(host)
entity_id = util.ensure_unique_string(
ENTITY_ID_FORMAT.format(
util.slugify(cast.device.friendly_name)),
casts.keys())
casts[entity_id] = cast
except pychromecast.ChromecastConnectionError:
pass
def setup(hass, config):
# pylint: disable=unused-argument,too-many-locals
""" Listen for chromecast events. """
logger = logging.getLogger(__name__)
discovery = get_component('discovery')
try:
# pylint: disable=redefined-outer-name
import pychromecast
except ImportError:
logger.exception(("Failed to import pychromecast. "
"Did you maybe not install the 'pychromecast' "
"dependency?"))
return False
casts = {}
# If discovery component not loaded, scan ourselves
if discovery.DOMAIN not in hass.components:
logger.info("Scanning for Chromecasts")
hosts = pychromecast.discover_chromecasts()
for host in hosts:
setup_chromecast(casts, host)
def chromecast_discovered(service, info):
""" Called when a Chromecast has been discovered. """
logger.info("New Chromecast discovered: %s", info[0])
setup_chromecast(casts, info[0])
discovery.listen(
hass, discovery.services.GOOGLE_CAST, chromecast_discovered)
def update_chromecast_state(entity_id, chromecast):
""" Retrieve state of Chromecast and update statemachine. """
chromecast.refresh()
status = chromecast.app
state_attr = {ATTR_FRIENDLY_NAME:
chromecast.device.friendly_name}
if status and status.app_id != pychromecast.APP_ID['HOME']:
state = status.app_id
ramp = chromecast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp and ramp.state != pychromecast.RAMP_STATE_UNKNOWN:
if ramp.state == pychromecast.RAMP_STATE_PLAYING:
state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_PLAYING
else:
state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_STOPPED
if ramp.content_id:
state_attr[ATTR_MEDIA_CONTENT_ID] = ramp.content_id
if ramp.title:
state_attr[ATTR_MEDIA_TITLE] = ramp.title
if ramp.artist:
state_attr[ATTR_MEDIA_ARTIST] = ramp.artist
if ramp.album:
state_attr[ATTR_MEDIA_ALBUM] = ramp.album
if ramp.image_url:
state_attr[ATTR_MEDIA_IMAGE_URL] = ramp.image_url
if ramp.duration:
state_attr[ATTR_MEDIA_DURATION] = ramp.duration
state_attr[ATTR_MEDIA_VOLUME] = ramp.volume
else:
state = STATE_NO_APP
hass.states.set(entity_id, state, state_attr)
def update_chromecast_states(time):
""" Updates all chromecast states. """
if casts:
logger.info("Updating Chromecast status")
for entity_id, cast in casts.items():
update_chromecast_state(entity_id, cast)
def _service_to_entities(service):
""" Helper method to get entities from service. """
entity_ids = extract_entity_ids(hass, service)
if entity_ids:
for entity_id in entity_ids:
cast = casts.get(entity_id)
if cast:
yield entity_id, cast
else:
yield from casts.items()
def turn_off_service(service):
""" Service to exit any running app on the specified ChromeCast and
shows idle screen. Will quit all ChromeCasts if nothing specified.
"""
for entity_id, cast in _service_to_entities(service):
cast.quit_app()
update_chromecast_state(entity_id, cast)
def volume_up_service(service):
""" Service to send the chromecast the command for volume up. """
for _, cast in _service_to_entities(service):
ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp:
ramp.volume_up()
def volume_down_service(service):
""" Service to send the chromecast the command for volume down. """
for _, cast in _service_to_entities(service):
ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp:
ramp.volume_down()
def media_play_pause_service(service):
""" Service to send the chromecast the command for play/pause. """
for _, cast in _service_to_entities(service):
ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp:
ramp.playpause()
def media_play_service(service):
""" Service to send the chromecast the command for play/pause. """
for _, cast in _service_to_entities(service):
ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp and ramp.state == pychromecast.RAMP_STATE_STOPPED:
ramp.playpause()
def media_pause_service(service):
""" Service to send the chromecast the command for play/pause. """
for _, cast in _service_to_entities(service):
ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp and ramp.state == pychromecast.RAMP_STATE_PLAYING:
ramp.playpause()
def media_next_track_service(service):
""" Service to send the chromecast the command for next track. """
for entity_id, cast in _service_to_entities(service):
ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp:
next(ramp)
update_chromecast_state(entity_id, cast)
def play_youtube_video_service(service, video_id):
""" Plays specified video_id on the Chromecast's YouTube channel. """
if video_id: # if service.data.get('video') returned None
for entity_id, cast in _service_to_entities(service):
pychromecast.play_youtube_video(video_id, cast.host)
update_chromecast_state(entity_id, cast)
hass.track_time_change(update_chromecast_states, second=range(0, 60, 15))
hass.services.register(DOMAIN, SERVICE_TURN_OFF,
turn_off_service)
hass.services.register(DOMAIN, SERVICE_VOLUME_UP,
volume_up_service)
hass.services.register(DOMAIN, SERVICE_VOLUME_DOWN,
volume_down_service)
hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE,
media_play_pause_service)
hass.services.register(DOMAIN, SERVICE_MEDIA_PLAY,
media_play_service)
hass.services.register(DOMAIN, SERVICE_MEDIA_PAUSE,
media_pause_service)
hass.services.register(DOMAIN, SERVICE_MEDIA_NEXT_TRACK,
media_next_track_service)
hass.services.register(DOMAIN, "start_fireplace",
lambda service:
play_youtube_video_service(service, "eyU3bRy2x44"))
hass.services.register(DOMAIN, "start_epic_sax",
lambda service:
play_youtube_video_service(service, "kxopViU98Xo"))
hass.services.register(DOMAIN, SERVICE_YOUTUBE_VIDEO,
lambda service:
play_youtube_video_service(service,
service.data.get(
'video')))
update_chromecast_states(None)
return True

View File

@ -17,7 +17,8 @@ DOMAIN = "demo"
DEPENDENCIES = [] DEPENDENCIES = []
COMPONENTS_WITH_DEMO_PLATFORM = ['switch', 'light', 'thermostat', 'sensor'] COMPONENTS_WITH_DEMO_PLATFORM = [
'switch', 'light', 'thermostat', 'sensor', 'media_player']
def setup(hass, config): def setup(hass, config):
@ -71,12 +72,6 @@ def setup(hass, config):
] ]
}) })
# Setup chromecast
hass.states.set("chromecast.Living_Rm", "Plex",
{'friendly_name': 'Living Room',
ATTR_ENTITY_PICTURE:
'http://graph.facebook.com/KillBillMovie/picture'})
# Setup configurator # Setup configurator
configurator_ids = [] configurator_ids = []

View File

@ -16,7 +16,7 @@ import homeassistant.util as util
from homeassistant.const import ( from homeassistant.const import (
STATE_HOME, STATE_NOT_HOME, ATTR_ENTITY_PICTURE, ATTR_FRIENDLY_NAME, STATE_HOME, STATE_NOT_HOME, ATTR_ENTITY_PICTURE, ATTR_FRIENDLY_NAME,
CONF_PLATFORM, CONF_TYPE) CONF_PLATFORM)
from homeassistant.components import group from homeassistant.components import group
DOMAIN = "device_tracker" DOMAIN = "device_tracker"
@ -53,21 +53,11 @@ def is_on(hass, entity_id=None):
def setup(hass, config): def setup(hass, config):
""" Sets up the device tracker. """ """ Sets up the device tracker. """
# CONF_TYPE is deprecated for CONF_PLATOFRM. We keep supporting it for now. if not validate_config(config, {DOMAIN: [CONF_PLATFORM]}, _LOGGER):
if not (validate_config(config, {DOMAIN: [CONF_PLATFORM]}, _LOGGER) or
validate_config(config, {DOMAIN: [CONF_TYPE]}, _LOGGER)):
return False return False
tracker_type = config[DOMAIN].get(CONF_PLATFORM) tracker_type = config[DOMAIN].get(CONF_PLATFORM)
if tracker_type is None:
tracker_type = config[DOMAIN][CONF_TYPE]
_LOGGER.warning((
"Please update your config for %s to use 'platform' "
"instead of 'type'"), tracker_type)
tracker_implementation = get_component( tracker_implementation = get_component(
'device_tracker.{}'.format(tracker_type)) 'device_tracker.{}'.format(tracker_type))

View File

@ -25,7 +25,7 @@ SCAN_INTERVAL = 300 # seconds
SERVICE_HANDLERS = { SERVICE_HANDLERS = {
services.BELKIN_WEMO: "switch", services.BELKIN_WEMO: "switch",
services.GOOGLE_CAST: "chromecast", services.GOOGLE_CAST: "media_player",
services.PHILIPS_HUE: "light", services.PHILIPS_HUE: "light",
} }

View File

@ -15,6 +15,8 @@ from homeassistant.const import URL_ROOT, HTTP_OK
DOMAIN = 'frontend' DOMAIN = 'frontend'
DEPENDENCIES = ['api'] DEPENDENCIES = ['api']
INDEX_PATH = os.path.join(os.path.dirname(__file__), 'index.html.template')
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -40,10 +42,6 @@ def setup(hass, config):
def _handle_get_root(handler, path_match, data): def _handle_get_root(handler, path_match, data):
""" Renders the debug interface. """ """ Renders the debug interface. """
def write(txt):
""" Helper to write text to the output. """
handler.wfile.write((txt + "\n").encode("UTF-8"))
handler.send_response(HTTP_OK) handler.send_response(HTTP_OK)
handler.send_header('Content-type', 'text/html; charset=utf-8') handler.send_header('Content-type', 'text/html; charset=utf-8')
handler.end_headers() handler.end_headers()
@ -54,28 +52,16 @@ def _handle_get_root(handler, path_match, data):
app_url = "frontend-{}.html".format(version.VERSION) app_url = "frontend-{}.html".format(version.VERSION)
# auto login if no password was set, else check api_password param # auto login if no password was set, else check api_password param
auth = (handler.server.api_password if handler.server.no_password_set auth = ('no_password_set' if handler.server.no_password_set
else data.get('api_password', '')) else data.get('api_password', ''))
write(("<!doctype html>" with open(INDEX_PATH) as template_file:
"<html>" template_html = template_file.read()
"<head><title>Home Assistant</title>"
"<meta name='mobile-web-app-capable' content='yes'>" template_html = template_html.replace('{{ app_url }}', app_url)
"<link rel='shortcut icon' href='/static/favicon.ico' />" template_html = template_html.replace('{{ auth }}', auth)
"<link rel='icon' type='image/png' "
" href='/static/favicon-192x192.png' sizes='192x192'>" handler.wfile.write(template_html.encode("UTF-8"))
"<meta name='viewport' content='width=device-width, "
" user-scalable=no, initial-scale=1.0, "
" minimum-scale=1.0, maximum-scale=1.0' />"
"<meta name='theme-color' content='#03a9f4'>"
"</head>"
"<body fullbleed>"
"<h3 id='init' align='center'>Initializing Home Assistant</h3>"
"<script"
" src='/static/webcomponents.min.js'></script>"
"<link rel='import' href='/static/{}' />"
"<home-assistant auth='{}'></home-assistant>"
"</body></html>").format(app_url, auth))
def _handle_get_static(handler, path_match, data): def _handle_get_static(handler, path_match, data):

View File

@ -0,0 +1,28 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Home Assistant</title>
<link rel='manifest' href='/static/manifest.json' />
<meta name='apple-mobile-web-app-capable' content='yes'>
<meta name='mobile-web-app-capable' content='yes'>
<meta name='viewport' content='width=device-width,
user-scalable=no' />
<link rel='shortcut icon' href='/static/favicon.ico' />
<link rel='icon' type='image/png'
href='/static/favicon-192x192.png' sizes='192x192'>
<link rel='apple-touch-icon' sizes='180x180'
href='/apple-icon-180x180.png'>
<meta name='theme-color' content='#03a9f4'>
</head>
<body fullbleed>
<h3 id='init' align='center'>Initializing Home Assistant</h3>
<script src='/static/webcomponents.min.js'></script>
<link rel='import' href='/static/{{ app_url }}' />
<home-assistant auth='{{ auth }}'></home-assistant>
</body>
</html>

View File

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """ """ DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "30729f23c19a66d9364d78b2379867cc" VERSION = "1c265f0f07e6038c2cbb9b277e58b994"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,14 @@
{
"name": "Home Assistant",
"short_name": "Assistant",
"start_url": "/",
"display": "standalone",
"icons": [
{
"src": "\/static\/favicon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

View File

@ -22,6 +22,7 @@
"core-icons": "polymer/core-icons#~0.5.5", "core-icons": "polymer/core-icons#~0.5.5",
"core-image": "polymer/core-image#~0.5.5", "core-image": "polymer/core-image#~0.5.5",
"core-style": "polymer/core-style#~0.5.5", "core-style": "polymer/core-style#~0.5.5",
"core-label": "polymer/core-label#~0.5.5",
"paper-toast": "Polymer/paper-toast#~0.5.5", "paper-toast": "Polymer/paper-toast#~0.5.5",
"paper-dialog": "Polymer/paper-dialog#~0.5.5", "paper-dialog": "Polymer/paper-dialog#~0.5.5",
"paper-spinner": "Polymer/paper-spinner#~0.5.5", "paper-spinner": "Polymer/paper-spinner#~0.5.5",
@ -33,6 +34,7 @@
"paper-dropdown": "polymer/paper-dropdown#~0.5.5", "paper-dropdown": "polymer/paper-dropdown#~0.5.5",
"paper-item": "polymer/paper-item#~0.5.5", "paper-item": "polymer/paper-item#~0.5.5",
"paper-slider": "polymer/paper-slider#~0.5.5", "paper-slider": "polymer/paper-slider#~0.5.5",
"paper-checkbox": "polymer/paper-checkbox#~0.5.5",
"color-picker-element": "~0.0.2", "color-picker-element": "~0.0.2",
"google-apis": "GoogleWebComponents/google-apis#~0.4.2", "google-apis": "GoogleWebComponents/google-apis#~0.4.2",
"core-drawer-panel": "polymer/core-drawer-panel#~0.5.5", "core-drawer-panel": "polymer/core-drawer-panel#~0.5.5",

View File

@ -29,7 +29,7 @@
case "switch": case "switch":
return "image:flash-on"; return "image:flash-on";
case "chromecast": case "media_player":
var icon = "hardware:cast"; var icon = "hardware:cast";
if (state !== "idle") { if (state !== "idle") {
@ -47,9 +47,6 @@
case "light": case "light":
return "image:wb-incandescent"; return "image:wb-incandescent";
case "tellstick_sensor":
return "trending-up";
case "simple_alarm": case "simple_alarm":
return "social:notifications"; return "social:notifications";

@ -1 +1 @@
Subproject commit 2f96b21d42c6b1c01a235b39fbbd2e0cf1b8d651 Subproject commit 8ea3a9e858a8c39d4c3aa46b719362b33f4a358f

View File

@ -30,8 +30,9 @@
</template> </template>
<script> <script>
var storeListenerMixIn = window.hass.storeListenerMixIn; var storeListenerMixIn = window.hass.storeListenerMixIn,
var uiActions = window.hass.uiActions; uiActions = window.hass.uiActions,
preferenceStore = window.hass.preferenceStore;
Polymer(Polymer.mixin({ Polymer(Polymer.mixin({
loaded: false, loaded: false,
@ -42,7 +43,9 @@
// if auth was given, tell the backend // if auth was given, tell the backend
if(this.auth) { if(this.auth) {
uiActions.validateAuth(this.auth); uiActions.validateAuth(this.auth, false);
} else if (preferenceStore.hasAuthToken()) {
uiActions.validateAuth(preferenceStore.getAuthToken(), false);
} }
}, },

View File

@ -1,5 +1,7 @@
<link rel="import" href="../bower_components/polymer/polymer.html"> <link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/core-label/core-label.html">
<link rel="import" href="../bower_components/paper-checkbox/paper-checkbox.html">
<link rel="import" href="../bower_components/paper-button/paper-button.html"> <link rel="import" href="../bower_components/paper-button/paper-button.html">
<link rel="import" href="../bower_components/paper-input/paper-input-decorator.html"> <link rel="import" href="../bower_components/paper-input/paper-input-decorator.html">
<link rel="import" href="../bower_components/core-input/core-input.html"> <link rel="import" href="../bower_components/core-input/core-input.html">
@ -8,15 +10,29 @@
<polymer-element name="login-form"> <polymer-element name="login-form">
<template> <template>
<style> <style>
paper-input { #passwordDecorator {
display: block; display: block;
height: 57px;
} }
.login paper-button { paper-checkbox {
margin-left: 242px; margin-right: 8px;
} }
.login .interact { paper-checkbox::shadow #checkbox.checked {
background-color: #03a9f4;
border-color: #03a9f4;
}
paper-checkbox::shadow #ink[checked] {
color: #03a9f4;
}
paper-button {
margin-left: 72px;
}
.interact {
height: 125px; height: 125px;
} }
@ -44,7 +60,14 @@
<input is="core-input" type="password" id="passwordInput" <input is="core-input" type="password" id="passwordInput"
value="{{authToken}}" on-keyup="{{passwordKeyup}}"> value="{{authToken}}" on-keyup="{{passwordKeyup}}">
</paper-input-decorator> </paper-input-decorator>
<paper-button on-click={{validatePassword}}>Log In</paper-button>
<div horizontal center layout>
<core-label horizontal layout>
<paper-checkbox for checked={{rememberLogin}}></paper-checkbox><b>Remember</b>
</core-label>
<paper-button on-click={{validatePassword}}>Log In</paper-button>
</div>
</div> </div>
<div id="validatebox" hidden?="{{!(isValidating || isLoggedIn)}}"> <div id="validatebox" hidden?="{{!(isValidating || isLoggedIn)}}">
@ -66,6 +89,7 @@
MSG_LOADING_DATA: "Loading data…", MSG_LOADING_DATA: "Loading data…",
authToken: "", authToken: "",
rememberLogin: false,
isValidating: false, isValidating: false,
isLoggedIn: false, isLoggedIn: false,
@ -87,7 +111,7 @@
this.spinnerMessage = this.isValidating ? this.MSG_VALIDATING : this.MSG_LOADING_DATA; this.spinnerMessage = this.isValidating ? this.MSG_VALIDATING : this.MSG_LOADING_DATA;
if (authStore.wasLastAttemptInvalid()) { if (authStore.wasLastAttemptInvalid()) {
this.$.passwordDecorator.error = this.authStore.getLastAttemptMessage(); this.$.passwordDecorator.error = authStore.getLastAttemptMessage();
this.$.passwordDecorator.isInvalid = true; this.$.passwordDecorator.isInvalid = true;
} }
@ -114,7 +138,7 @@
validatePassword: function() { validatePassword: function() {
this.$.hideKeyboardOnFocus.focus(); this.$.hideKeyboardOnFocus.focus();
uiActions.validateAuth(this.authToken); uiActions.validateAuth(this.authToken, this.rememberLogin);
}, },
}, storeListenerMixIn)); }, storeListenerMixIn));
</script> </script>

View File

@ -61,8 +61,11 @@
}); });
}, },
validateAuth: function(authToken) { validateAuth: function(authToken, rememberLogin) {
authActions.validate(authToken, preferenceStore.useStreaming()); authActions.validate(authToken, {
useStreaming: preferenceStore.useStreaming(),
rememberLogin: rememberLogin,
});
}, },
}; };
})(); })();

View File

@ -111,7 +111,7 @@ def setup(hass, config=None):
if config is None or DOMAIN not in config: if config is None or DOMAIN not in config:
config = {DOMAIN: {}} config = {DOMAIN: {}}
api_password = config[DOMAIN].get(CONF_API_PASSWORD) api_password = str(config[DOMAIN].get(CONF_API_PASSWORD))
no_password_set = api_password is None no_password_set = api_password is None
@ -123,7 +123,7 @@ def setup(hass, config=None):
server_port = config[DOMAIN].get(CONF_SERVER_PORT, SERVER_PORT) server_port = config[DOMAIN].get(CONF_SERVER_PORT, SERVER_PORT)
development = config[DOMAIN].get(CONF_DEVELOPMENT, "") == "1" development = str(config[DOMAIN].get(CONF_DEVELOPMENT, "")) == "1"
server = HomeAssistantHTTPServer( server = HomeAssistantHTTPServer(
(server_host, server_port), RequestHandler, hass, api_password, (server_host, server_port), RequestHandler, hass, api_password,
@ -341,17 +341,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
self.send_response(HTTP_OK) self.send_response(HTTP_OK)
self.send_header(HTTP_HEADER_CONTENT_TYPE, content_type) self.send_header(HTTP_HEADER_CONTENT_TYPE, content_type)
# Add cache if not development self.set_cache_header()
if not self.server.development:
# 1 year in seconds
cache_time = 365 * 86400
self.send_header(
HTTP_HEADER_CACHE_CONTROL,
"public, max-age={}".format(cache_time))
self.send_header(
HTTP_HEADER_EXPIRES,
self.date_time_string(time.time()+cache_time))
if do_gzip: if do_gzip:
gzip_data = gzip.compress(inp.read()) gzip_data = gzip.compress(inp.read())
@ -374,3 +364,16 @@ class RequestHandler(SimpleHTTPRequestHandler):
else: else:
self.copyfile(inp, self.wfile) self.copyfile(inp, self.wfile)
def set_cache_header(self):
""" Add cache headers if not in development """
if not self.server.development:
# 1 year in seconds
cache_time = 365 * 86400
self.send_header(
HTTP_HEADER_CACHE_CONTROL,
"public, max-age={}".format(cache_time))
self.send_header(
HTTP_HEADER_EXPIRES,
self.date_time_string(time.time()+cache_time))

View File

@ -57,7 +57,6 @@ from homeassistant.helpers.device_component import DeviceComponent
import homeassistant.util as util 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 extract_entity_ids
from homeassistant.components import group, discovery, wink from homeassistant.components import group, discovery, wink
@ -146,8 +145,6 @@ def setup(hass, config):
GROUP_NAME_ALL_LIGHTS) GROUP_NAME_ALL_LIGHTS)
component.setup(config) component.setup(config)
lights = component.devices
# Load built-in profiles and custom profiles # Load built-in profiles and custom profiles
profile_paths = [os.path.join(os.path.dirname(__file__), profile_paths = [os.path.join(os.path.dirname(__file__),
LIGHT_PROFILES_FILE), LIGHT_PROFILES_FILE),
@ -182,12 +179,7 @@ def setup(hass, config):
dat = service.data dat = service.data
# Convert the entity ids to valid light ids # Convert the entity ids to valid light ids
target_lights = [lights[entity_id] for entity_id target_lights = component.extract_from_service(service)
in extract_entity_ids(hass, service)
if entity_id in lights]
if not target_lights:
target_lights = lights.values()
params = {} params = {}

View File

@ -1,7 +1,7 @@
""" Provides demo lights. """ """ Provides demo lights. """
import random import random
from homeassistant.helpers import ToggleDevice from homeassistant.helpers.device import ToggleDevice
from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME
from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_XY_COLOR from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_XY_COLOR

View File

@ -6,14 +6,14 @@ from urllib.parse import urlparse
from homeassistant.loader import get_component from homeassistant.loader import get_component
import homeassistant.util as util import homeassistant.util as util
from homeassistant.helpers import ToggleDevice from homeassistant.helpers.device import ToggleDevice
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION, ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION,
ATTR_FLASH, FLASH_LONG, FLASH_SHORT) ATTR_FLASH, FLASH_LONG, FLASH_SHORT)
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100)
PHUE_CONFIG_FILE = "phue.conf" PHUE_CONFIG_FILE = "phue.conf"

View File

@ -3,7 +3,7 @@ import logging
# pylint: disable=no-name-in-module, import-error # pylint: disable=no-name-in-module, import-error
from homeassistant.components.light import ATTR_BRIGHTNESS from homeassistant.components.light import ATTR_BRIGHTNESS
from homeassistant.const import ATTR_FRIENDLY_NAME from homeassistant.const import ATTR_FRIENDLY_NAME
from homeassistant.helpers import ToggleDevice from homeassistant.helpers.device import ToggleDevice
import tellcore.constants as tellcore_constants import tellcore.constants as tellcore_constants

View File

@ -0,0 +1,207 @@
"""
homeassistant.components.media_player
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.const import (
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_VOLUME_UP,
SERVICE_VOLUME_DOWN, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY,
SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREV_TRACK)
DOMAIN = 'media_player'
DEPENDENCIES = []
SCAN_INTERVAL = 30
ENTITY_ID_FORMAT = DOMAIN + '.{}'
DISCOVERY_PLATFORMS = {
discovery.services.GOOGLE_CAST: 'cast',
}
SERVICE_YOUTUBE_VIDEO = 'play_youtube_video'
STATE_NO_APP = 'idle'
ATTR_STATE = 'state'
ATTR_OPTIONS = 'options'
ATTR_MEDIA_STATE = 'media_state'
ATTR_MEDIA_CONTENT_ID = 'media_content_id'
ATTR_MEDIA_TITLE = 'media_title'
ATTR_MEDIA_ARTIST = 'media_artist'
ATTR_MEDIA_ALBUM = 'media_album'
ATTR_MEDIA_IMAGE_URL = 'media_image_url'
ATTR_MEDIA_VOLUME = 'media_volume'
ATTR_MEDIA_DURATION = 'media_duration'
MEDIA_STATE_UNKNOWN = 'unknown'
MEDIA_STATE_PLAYING = 'playing'
MEDIA_STATE_STOPPED = 'stopped'
YOUTUBE_COVER_URL_FORMAT = 'http://img.youtube.com/vi/{}/1.jpg'
def is_on(hass, entity_id=None):
""" Returns true if specified media player entity_id is on.
Will check all media player if no entity_id specified. """
entity_ids = [entity_id] if entity_id else hass.states.entity_ids(DOMAIN)
return any(not hass.states.is_state(entity_id, STATE_NO_APP)
for entity_id in entity_ids)
def turn_off(hass, entity_id=None):
""" Will turn off specified media player or all. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
def volume_up(hass, entity_id=None):
""" Send the media player the command for volume up. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_VOLUME_UP, data)
def volume_down(hass, entity_id=None):
""" Send the media player the command for volume down. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_VOLUME_DOWN, data)
def media_play_pause(hass, entity_id=None):
""" Send the media player the command for play/pause. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PLAY_PAUSE, data)
def media_play(hass, entity_id=None):
""" Send the media player the command for play/pause. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PLAY, data)
def media_pause(hass, entity_id=None):
""" Send the media player the command for play/pause. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PAUSE, data)
def media_next_track(hass, entity_id=None):
""" Send the media player the command for next track. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_NEXT_TRACK, data)
def media_prev_track(hass, entity_id=None):
""" Send the media player the command for prev track. """
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
hass.services.call(DOMAIN, SERVICE_MEDIA_PREV_TRACK, data)
SERVICE_TO_METHOD = {
SERVICE_TURN_OFF: 'turn_off',
SERVICE_VOLUME_UP: 'volume_up',
SERVICE_VOLUME_DOWN: 'volume_down',
SERVICE_MEDIA_PLAY_PAUSE: 'media_play_pause',
SERVICE_MEDIA_PLAY: 'media_play',
SERVICE_MEDIA_PAUSE: 'media_pause',
SERVICE_MEDIA_NEXT_TRACK: 'media_next_track',
}
def setup(hass, config):
""" Track states and offer events for media_players. """
component = DeviceComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL,
DISCOVERY_PLATFORMS)
component.setup(config)
def media_player_service_handler(service):
""" Maps services to methods on MediaPlayerDevice. """
target_players = component.extract_from_service(service)
method = SERVICE_TO_METHOD[service.service]
for player in target_players:
getattr(player, method)()
if player.should_poll:
player.update_ha_state(True)
for service in SERVICE_TO_METHOD:
hass.services.register(DOMAIN, service, media_player_service_handler)
def play_youtube_video_service(service, media_id):
""" Plays specified media_id on the media player. """
target_players = component.extract_from_service(service)
if media_id:
for player in target_players:
player.play_youtube(media_id)
hass.services.register(DOMAIN, "start_fireplace",
lambda service:
play_youtube_video_service(service, "eyU3bRy2x44"))
hass.services.register(DOMAIN, "start_epic_sax",
lambda service:
play_youtube_video_service(service, "kxopViU98Xo"))
hass.services.register(DOMAIN, SERVICE_YOUTUBE_VIDEO,
lambda service:
play_youtube_video_service(
service, service.data.get('video')))
return True
class MediaPlayerDevice(Device):
""" ABC for media player devices. """
def turn_off(self):
""" turn_off media player. """
pass
def volume_up(self):
""" volume_up media player. """
pass
def volume_down(self):
""" volume_down media player. """
pass
def media_play_pause(self):
""" media_play_pause media player. """
pass
def media_play(self):
""" media_play media player. """
pass
def media_pause(self):
""" media_pause media player. """
pass
def media_next_track(self):
""" media_next_track media player. """
pass
def play_youtube(self, media_id):
""" Plays a YouTube media. """
pass

View File

@ -0,0 +1,162 @@
"""
homeassistant.components.media_player.chromecast
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to interact with Cast devices on the network.
WARNING: This platform is currently not working due to a changed Cast API
"""
import logging
try:
import pychromecast
except ImportError:
# We will throw error later
pass
from homeassistant.components.media_player import (
MediaPlayerDevice, STATE_NO_APP, ATTR_MEDIA_STATE,
ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_TITLE, ATTR_MEDIA_ARTIST,
ATTR_MEDIA_ALBUM, ATTR_MEDIA_IMAGE_URL, ATTR_MEDIA_DURATION,
ATTR_MEDIA_VOLUME, MEDIA_STATE_PLAYING, MEDIA_STATE_STOPPED)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the cast platform. """
logger = logging.getLogger(__name__)
try:
# pylint: disable=redefined-outer-name
import pychromecast
except ImportError:
logger.exception(("Failed to import pychromecast. "
"Did you maybe not install the 'pychromecast' "
"dependency?"))
return
if discovery_info:
hosts = [discovery_info[0]]
else:
hosts = pychromecast.discover_chromecasts()
casts = []
for host in hosts:
try:
casts.append(CastDevice(host))
except pychromecast.ChromecastConnectionError:
pass
add_devices(casts)
class CastDevice(MediaPlayerDevice):
""" Represents a Cast device on the network. """
def __init__(self, host):
self.cast = pychromecast.PyChromecast(host)
@property
def name(self):
""" Returns the name of the device. """
return self.cast.device.friendly_name
@property
def state(self):
""" Returns the state of the device. """
status = self.cast.app
if status is None or status.app_id == pychromecast.APP_ID['HOME']:
return STATE_NO_APP
else:
return status.description
@property
def state_attributes(self):
""" Returns the state attributes. """
ramp = self.cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp and ramp.state != pychromecast.RAMP_STATE_UNKNOWN:
state_attr = {}
if ramp.state == pychromecast.RAMP_STATE_PLAYING:
state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_PLAYING
else:
state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_STOPPED
if ramp.content_id:
state_attr[ATTR_MEDIA_CONTENT_ID] = ramp.content_id
if ramp.title:
state_attr[ATTR_MEDIA_TITLE] = ramp.title
if ramp.artist:
state_attr[ATTR_MEDIA_ARTIST] = ramp.artist
if ramp.album:
state_attr[ATTR_MEDIA_ALBUM] = ramp.album
if ramp.image_url:
state_attr[ATTR_MEDIA_IMAGE_URL] = ramp.image_url
if ramp.duration:
state_attr[ATTR_MEDIA_DURATION] = ramp.duration
state_attr[ATTR_MEDIA_VOLUME] = ramp.volume
return state_attr
def turn_off(self):
""" Service to exit any running app on the specimedia player ChromeCast and
shows idle screen. Will quit all ChromeCasts if nothing specified.
"""
self.cast.quit_app()
def volume_up(self):
""" Service to send the chromecast the command for volume up. """
ramp = self.cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp:
ramp.volume_up()
def volume_down(self):
""" Service to send the chromecast the command for volume down. """
ramp = self.cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp:
ramp.volume_down()
def media_play_pause(self):
""" Service to send the chromecast the command for play/pause. """
ramp = self.cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp:
ramp.playpause()
def media_play(self):
""" Service to send the chromecast the command for play/pause. """
ramp = self.cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp and ramp.state == pychromecast.RAMP_STATE_STOPPED:
ramp.playpause()
def media_pause(self):
""" Service to send the chromecast the command for play/pause. """
ramp = self.cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp and ramp.state == pychromecast.RAMP_STATE_PLAYING:
ramp.playpause()
def media_next_track(self):
""" Service to send the chromecast the command for next track. """
ramp = self.cast.get_protocol(pychromecast.PROTOCOL_RAMP)
if ramp:
ramp.next()
def play_youtube_video(self, video_id):
""" Plays specified video_id on the Chromecast's YouTube channel. """
pychromecast.play_youtube_video(video_id, self.cast.host)

View File

@ -0,0 +1,100 @@
"""
homeassistant.components.media_player.chromecast
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Demo implementation of the media player.
"""
from homeassistant.components.media_player import (
MediaPlayerDevice, STATE_NO_APP, ATTR_MEDIA_STATE,
ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_TITLE, ATTR_MEDIA_DURATION,
ATTR_MEDIA_VOLUME, MEDIA_STATE_PLAYING, MEDIA_STATE_STOPPED,
YOUTUBE_COVER_URL_FORMAT)
from homeassistant.const import ATTR_ENTITY_PICTURE
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the cast platform. """
add_devices([
DemoMediaPlayer(
'Living Room', 'eyU3bRy2x44',
'♥♥ The Best Fireplace Video (3 hours)'),
DemoMediaPlayer('Bedroom', 'kxopViU98Xo', 'Epic sax guy 10 hours')
])
class DemoMediaPlayer(MediaPlayerDevice):
""" A Demo media player that only supports YouTube. """
def __init__(self, name, youtube_id=None, media_title=None):
self._name = name
self.is_playing = youtube_id is not None
self.youtube_id = youtube_id
self.media_title = media_title
self.volume = 1.0
@property
def name(self):
""" Returns the name of the device. """
return self._name
@property
def state(self):
""" Returns the state of the device. """
return STATE_NO_APP if self.youtube_id is None else "YouTube"
@property
def state_attributes(self):
""" Returns the state attributes. """
if self.youtube_id is None:
return
state_attr = {
ATTR_MEDIA_CONTENT_ID: self.youtube_id,
ATTR_MEDIA_TITLE: self.media_title,
ATTR_MEDIA_DURATION: 100,
ATTR_MEDIA_VOLUME: self.volume,
ATTR_ENTITY_PICTURE:
YOUTUBE_COVER_URL_FORMAT.format(self.youtube_id)
}
if self.is_playing:
state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_PLAYING
else:
state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_STOPPED
return state_attr
def turn_off(self):
""" turn_off media player. """
self.youtube_id = None
self.is_playing = False
def volume_up(self):
""" volume_up media player. """
if self.volume < 1:
self.volume += 0.1
def volume_down(self):
""" volume_down media player. """
if self.volume > 0:
self.volume -= 0.1
def media_play_pause(self):
""" media_play_pause media player. """
self.is_playing = not self.is_playing
def media_play(self):
""" media_play media player. """
self.is_playing = True
def media_pause(self):
""" media_pause media player. """
self.is_playing = False
def play_youtube(self, media_id):
""" Plays a YouTube media. """
self.youtube_id = media_id
self.media_title = 'Demo media title'
self.is_playing = True

View File

@ -1,5 +1,5 @@
""" Support for Wink sensors. """ """ Support for Wink sensors. """
from homeassistant.helpers import Device from homeassistant.helpers.device import Device
from homeassistant.const import ( from homeassistant.const import (
TEMP_CELCIUS, ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME) TEMP_CELCIUS, ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME)

View File

@ -0,0 +1,97 @@
"""
homeassistant.components.sensor.systemmonitor
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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)
import psutil
import logging
SENSOR_TYPES = {
'disk_use_percent': ['Disk Use', '%'],
'disk_use': ['Disk Use', 'GiB'],
'disk_free': ['Disk Free', 'GiB'],
'memory_use_percent': ['RAM Use', '%'],
'memory_use': ['RAM Use', 'MiB'],
'memory_free': ['RAM Free', 'MiB'],
'processor_use': ['CPU Use', '%'],
'process': ['Process', ''],
}
_LOGGER = logging.getLogger(__name__)
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the sensors """
dev = []
for resource in config['resources']:
if 'arg' not in resource:
resource['arg'] = ''
if resource['type'] not in SENSOR_TYPES:
_LOGGER.error('Sensor type: "%s" does not exist', resource['type'])
else:
dev.append(SystemMonitorSensor(resource['type'], resource['arg']))
add_devices(dev)
class SystemMonitorSensor(Device):
""" A system monitor sensor """
def __init__(self, sensor_type, argument=''):
self._name = SENSOR_TYPES[sensor_type][0] + ' ' + argument
self.argument = argument
self.type = sensor_type
self._state = None
self.unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self.update()
@property
def name(self):
return self._name
@property
def state(self):
""" Returns the state of the 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 update(self):
if self.type == 'disk_use_percent':
self._state = psutil.disk_usage(self.argument).percent
elif self.type == 'disk_use':
self._state = round(psutil.disk_usage(self.argument).used /
1024**3, 1)
elif self.type == 'disk_free':
self._state = round(psutil.disk_usage(self.argument).free /
1024**3, 1)
elif self.type == 'memory_use_percent':
self._state = psutil.virtual_memory().percent
elif self.type == 'memory_use':
self._state = round((psutil.virtual_memory().total -
psutil.virtual_memory().available) /
1024**2, 1)
elif self.type == 'memory_free':
self._state = round(psutil.virtual_memory().available / 1024**2, 1)
elif self.type == 'processor_use':
self._state = round(psutil.cpu_percent(interval=None))
elif self.type == 'process':
if any(self.argument in l.name() for l in psutil.process_iter()):
self._state = STATE_ON
else:
self._state = STATE_OFF

View File

@ -0,0 +1,126 @@
"""
homeassistant.components.sensor.tellstick
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Shows sensor values from tellstick sensors.
Possible config keys:
id of the sensor: Name the sensor with ID
135=Outside
only_named: Only show the named sensors
only_named=1
temperature_scale: The scale of the temperature value
temperature_scale=°C
datatype_mask: mask to determine which sensor values to show based on
https://tellcore-py.readthedocs.org
/en/v1.0.4/constants.html#module-tellcore.constants
datatype_mask=1 # only show temperature
datatype_mask=12 # only show rain rate and rain total
datatype_mask=127 # show all sensor values
"""
import logging
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
DatatypeDescription = namedtuple("DatatypeDescription", ['name', 'unit'])
# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up Tellstick sensors. """
sensor_value_descriptions = {
tellcore_constants.TELLSTICK_TEMPERATURE:
DatatypeDescription(
'temperature', config.get('temperature_scale', TEMP_CELCIUS)),
tellcore_constants.TELLSTICK_HUMIDITY:
DatatypeDescription('humidity', '%'),
tellcore_constants.TELLSTICK_RAINRATE:
DatatypeDescription('rain rate', ''),
tellcore_constants.TELLSTICK_RAINTOTAL:
DatatypeDescription('rain total', ''),
tellcore_constants.TELLSTICK_WINDDIRECTION:
DatatypeDescription('wind direction', ''),
tellcore_constants.TELLSTICK_WINDAVERAGE:
DatatypeDescription('wind average', ''),
tellcore_constants.TELLSTICK_WINDGUST:
DatatypeDescription('wind gust', '')
}
try:
core = telldus.TelldusCore()
except OSError:
logging.getLogger(__name__).exception(
'Could not initialize Tellstick.')
return
sensors = []
for ts_sensor in core.sensors():
try:
sensor_name = config[str(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):
sensor_info = sensor_value_descriptions[datatype]
sensors.append(
TellstickSensor(
sensor_name, ts_sensor, datatype, sensor_info))
add_devices(sensors)
class TellstickSensor(Device):
""" 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._name = "{} {}".format(name, sensor_info.name)
@property
def name(self):
""" Returns the name of the device. """
return self._name
@property
def state(self):
""" Returns the state of the 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

View File

@ -9,7 +9,7 @@ from openzwave.network import ZWaveNetwork
from pydispatch import dispatcher from pydispatch import dispatcher
import homeassistant.components.zwave as zwave import homeassistant.components.zwave as zwave
from homeassistant.helpers import Device from homeassistant.helpers.device import Device
from homeassistant.const import ( from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF, ATTR_BATTERY_LEVEL, ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF,
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION) TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION)

View File

@ -10,7 +10,6 @@ from homeassistant.helpers.device_component import DeviceComponent
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 extract_entity_ids
from homeassistant.components import group, discovery, wink from homeassistant.components import group, discovery, wink
DOMAIN = 'switch' DOMAIN = 'switch'
@ -64,16 +63,9 @@ def setup(hass, config):
GROUP_NAME_ALL_SWITCHES) GROUP_NAME_ALL_SWITCHES)
component.setup(config) component.setup(config)
switches = component.devices
def handle_switch_service(service): def handle_switch_service(service):
""" Handles calls to the switch services. """ """ Handles calls to the switch services. """
target_switches = [switches[entity_id] for entity_id target_switches = component.extract_from_service(service)
in extract_entity_ids(hass, service)
if entity_id in switches]
if not target_switches:
target_switches = switches.values()
for switch in target_switches: for switch in target_switches:
if service.service == SERVICE_TURN_ON: if service.service == SERVICE_TURN_ON:

View File

@ -1,5 +1,5 @@
""" Demo platform that has two fake switchces. """ """ Demo platform that has two fake switchces. """
from homeassistant.helpers import ToggleDevice from homeassistant.helpers.device import ToggleDevice
from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME

View File

@ -3,7 +3,7 @@ import logging
from homeassistant.const import ATTR_FRIENDLY_NAME from homeassistant.const import ATTR_FRIENDLY_NAME
from homeassistant.helpers import ToggleDevice from homeassistant.helpers.device import ToggleDevice
import tellcore.constants as tellcore_constants import tellcore.constants as tellcore_constants

View File

@ -1,7 +1,7 @@
""" Support for WeMo switchces. """ """ Support for WeMo switchces. """
import logging import logging
from homeassistant.helpers import ToggleDevice from homeassistant.helpers.device import ToggleDevice
from homeassistant.components.switch import ( from homeassistant.components.switch import (
ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH) ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH)

View File

@ -1,141 +0,0 @@
"""
homeassistant.components.tellstick_sensor
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Shows sensor values from tellstick sensors.
Possible config keys:
id of the sensor: Name the sensor with ID
135=Outside
only_named: Only show the named sensors
only_named=1
temperature_scale: The scale of the temperature value
temperature_scale=°C
datatype_mask: mask to determine which sensor values to show based on
https://tellcore-py.readthedocs.org
/en/v1.0.4/constants.html#module-tellcore.constants
datatype_mask=1 # only show temperature
datatype_mask=12 # only show rain rate and rain total
datatype_mask=127 # show all sensor values
"""
import logging
from collections import namedtuple
import homeassistant.util as util
from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_UNIT_OF_MEASUREMENT
# The domain of your component. Should be equal to the name of your component
DOMAIN = "tellstick_sensor"
# List of component names (string) your component depends upon
# If you are setting up a group but not using a group for anything,
# don't depend on group
DEPENDENCIES = []
ENTITY_ID_FORMAT = DOMAIN + '.{}'
DatatypeDescription = namedtuple("DatatypeDescription", ['name', 'unit'])
def setup(hass, config):
""" Register services or listen for events that your component needs. """
logger = logging.getLogger(__name__)
try:
import tellcore.telldus as telldus
import tellcore.constants as tellcore_constants
except ImportError:
logger.exception(
"Failed to import tellcore")
return False
core = telldus.TelldusCore()
sensors = core.sensors()
if len(sensors) == 0:
logger.error("No Tellstick sensors found")
return False
sensor_value_descriptions = {
tellcore_constants.TELLSTICK_TEMPERATURE:
DatatypeDescription(
'temperature', config[DOMAIN]['temperature_scale']),
tellcore_constants.TELLSTICK_HUMIDITY:
DatatypeDescription('humidity', ' %'),
tellcore_constants.TELLSTICK_RAINRATE:
DatatypeDescription('rain rate', ''),
tellcore_constants.TELLSTICK_RAINTOTAL:
DatatypeDescription('rain total', ''),
tellcore_constants.TELLSTICK_WINDDIRECTION:
DatatypeDescription('wind direction', ''),
tellcore_constants.TELLSTICK_WINDAVERAGE:
DatatypeDescription('wind average', ''),
tellcore_constants.TELLSTICK_WINDGUST:
DatatypeDescription('wind gust', '')
}
def update_sensor_value_state(sensor_name, sensor_value):
""" Update the state of a sensor value """
sensor_value_description = \
sensor_value_descriptions[sensor_value.datatype]
sensor_value_name = '{} {}'.format(
sensor_name, sensor_value_description.name)
entity_id = ENTITY_ID_FORMAT.format(
util.slugify(sensor_value_name))
state = sensor_value.value
state_attr = {
ATTR_FRIENDLY_NAME: sensor_value_name,
ATTR_UNIT_OF_MEASUREMENT: sensor_value_description.unit
}
hass.states.set(entity_id, state, state_attr)
sensor_value_datatypes = [
tellcore_constants.TELLSTICK_TEMPERATURE,
tellcore_constants.TELLSTICK_HUMIDITY,
tellcore_constants.TELLSTICK_RAINRATE,
tellcore_constants.TELLSTICK_RAINTOTAL,
tellcore_constants.TELLSTICK_WINDDIRECTION,
tellcore_constants.TELLSTICK_WINDAVERAGE,
tellcore_constants.TELLSTICK_WINDGUST
]
def update_sensor_state(sensor):
""" Updates all the sensor values from the sensor """
try:
sensor_name = config[DOMAIN][str(sensor.id)]
except KeyError:
if 'only_named' in config[DOMAIN]:
return
sensor_name = str(sensor.id)
for datatype in sensor_value_datatypes:
if datatype & int(config[DOMAIN]['datatype_mask']) and \
sensor.has_value(datatype):
update_sensor_value_state(sensor_name, sensor.value(datatype))
def update_sensors_state(time):
""" Update the state of all sensors """
for sensor in sensors:
update_sensor_state(sensor)
update_sensors_state(None)
hass.track_time_change(update_sensors_state, second=[0, 30])
return True

View File

@ -9,7 +9,7 @@ import logging
from homeassistant.helpers.device_component import DeviceComponent from homeassistant.helpers.device_component import DeviceComponent
import homeassistant.util as util import homeassistant.util as util
from homeassistant.helpers import Device, extract_entity_ids from homeassistant.helpers.device import Device
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT, ATTR_ENTITY_ID, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT,
STATE_ON, STATE_OFF) STATE_ON, STATE_OFF)
@ -56,18 +56,11 @@ def setup(hass, config):
component = DeviceComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL) component = DeviceComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
component.setup(config) component.setup(config)
thermostats = component.devices
def thermostat_service(service): def thermostat_service(service):
""" Handles calls to the services. """ """ Handles calls to the services. """
# Convert the entity ids to valid light ids # Convert the entity ids to valid light ids
target_thermostats = [thermostats[entity_id] for entity_id target_thermostats = component.extract_from_service(service)
in extract_entity_ids(hass, service)
if entity_id in thermostats]
if not target_thermostats:
target_thermostats = thermostats.values()
if service.service == SERVICE_SET_AWAY_MODE: if service.service == SERVICE_SET_AWAY_MODE:
away_mode = service.data.get(ATTR_AWAY_MODE) away_mode = service.data.get(ATTR_AWAY_MODE)

View File

@ -67,7 +67,7 @@ def setup(hass, config):
from openzwave.option import ZWaveOption from openzwave.option import ZWaveOption
from openzwave.network import ZWaveNetwork from openzwave.network import ZWaveNetwork
use_debug = config[DOMAIN].get(CONF_DEBUG) == '1' use_debug = str(config[DOMAIN].get(CONF_DEBUG)) == '1'
# Setup options # Setup options
options = ZWaveOption( options = ZWaveOption(

View File

@ -9,9 +9,6 @@ DEVICE_DEFAULT_NAME = "Unnamed Device"
CONF_LATITUDE = "latitude" CONF_LATITUDE = "latitude"
CONF_LONGITUDE = "longitude" CONF_LONGITUDE = "longitude"
# This one is deprecated. Use platform instead.
CONF_TYPE = "type"
CONF_PLATFORM = "platform" CONF_PLATFORM = "platform"
CONF_HOST = "host" CONF_HOST = "host"
CONF_HOSTS = "hosts" CONF_HOSTS = "hosts"

View File

@ -3,14 +3,14 @@ Helper methods for components within Home Assistant.
""" """
from datetime import datetime from datetime import datetime
from homeassistant import NoEntitySpecifiedError
from homeassistant.loader import get_component from homeassistant.loader import get_component
from homeassistant.const import ( from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, STATE_ON, STATE_OFF, CONF_PLATFORM,
CONF_TYPE, DEVICE_DEFAULT_NAME)
from homeassistant.util import ensure_unique_string, slugify from homeassistant.util import ensure_unique_string, slugify
# Deprecated 3/5/2015 - Moved to homeassistant.helpers.device
# pylint: disable=unused-import
from .device import Device, ToggleDevice # noqa
def generate_entity_id(entity_id_format, name, current_ids=None, hass=None): def generate_entity_id(entity_id_format, name, current_ids=None, hass=None):
""" Generate a unique entity ID based on given entity IDs or used ids. """ """ Generate a unique entity ID based on given entity IDs or used ids. """
@ -101,15 +101,6 @@ def config_per_platform(config, domain, logger):
platform_type = platform_config.get(CONF_PLATFORM) platform_type = platform_config.get(CONF_PLATFORM)
# DEPRECATED, still supported for now.
if platform_type is None:
platform_type = platform_config.get(CONF_TYPE)
if platform_type is not None:
logger.warning((
'Please update your config for {}.{} to use "platform" '
'instead of "type"').format(domain, platform_type))
if platform_type is None: if platform_type is None:
logger.warning('No platform specified for %s', config_key) logger.warning('No platform specified for %s', config_key)
break break
@ -118,169 +109,3 @@ def config_per_platform(config, domain, logger):
found += 1 found += 1
config_key = "{} {}".format(domain, found) config_key = "{} {}".format(domain, found)
def platform_devices_from_config(config, domain, hass,
entity_id_format, logger):
""" Parses the config for specified domain.
Loads different platforms and retrieve domains. """
devices = []
for p_type, p_config in config_per_platform(config, domain, logger):
platform = get_component('{}.{}'.format(domain, p_type))
if platform is None:
logger.error("Unknown %s type specified: %s", domain, p_type)
else:
try:
p_devices = platform.get_devices(hass, p_config)
except AttributeError:
# DEPRECATED, still supported for now
logger.warning(
'Platform %s should migrate to use the method get_devices',
p_type)
if domain == 'light':
p_devices = platform.get_lights(hass, p_config)
elif domain == 'switch':
p_devices = platform.get_switches(hass, p_config)
else:
raise
logger.info("Found %d %s %ss", len(p_devices), p_type, domain)
devices.extend(p_devices)
# Setup entity IDs for each device
device_dict = {}
no_name_count = 0
for device in devices:
device.hass = hass
# Get the name or set to default if none given
name = device.name or DEVICE_DEFAULT_NAME
if name == DEVICE_DEFAULT_NAME:
no_name_count += 1
name = "{} {}".format(domain, no_name_count)
entity_id = generate_entity_id(
entity_id_format, name, device_dict.keys())
device.entity_id = entity_id
device_dict[entity_id] = device
return device_dict
class Device(object):
""" ABC for Home Assistant devices. """
# pylint: disable=no-self-use
hass = None
entity_id = None
@property
def should_poll(self):
"""
Return True if device has to be polled for state.
False if device pushes its state to HA.
"""
return True
@property
def unique_id(self):
""" Returns a unique id. """
return "{}.{}".format(self.__class__, id(self))
@property
def name(self):
""" Returns the name of the device. """
return self.get_name()
@property
def state(self):
""" Returns the state of the device. """
return self.get_state()
@property
def state_attributes(self):
""" Returns the state attributes. """
return {}
# DEPRECATION NOTICE:
# Device is moving from getters to properties.
# For now the new properties will call the old functions
# This will be removed in the future.
def get_name(self):
""" Returns the name of the device if any. """
return DEVICE_DEFAULT_NAME
def get_state(self):
""" Returns state of the device. """
return "Unknown"
def get_state_attributes(self):
""" Returns optional state attributes. """
return None
def update(self):
""" Retrieve latest state from the real device. """
pass
def update_ha_state(self, force_refresh=False):
"""
Updates Home Assistant with current state of device.
If force_refresh == True will update device before setting state.
"""
if self.hass is None:
raise RuntimeError("Attribute hass is None for {}".format(self))
if self.entity_id is None:
raise NoEntitySpecifiedError(
"No entity specified for device {}".format(self.name))
if force_refresh:
self.update()
attr = self.state_attributes or {}
if ATTR_FRIENDLY_NAME not in attr and self.name:
attr[ATTR_FRIENDLY_NAME] = self.name
return self.hass.states.set(self.entity_id, self.state, attr)
def __eq__(self, other):
return (isinstance(other, Device) and
other.unique_id == self.unique_id)
def __repr__(self):
return "<Device {}: {}>".format(self.name, self.state)
class ToggleDevice(Device):
""" ABC for devices that can be turned on and off. """
# pylint: disable=no-self-use
@property
def state(self):
""" Returns the state. """
return STATE_ON if self.is_on else STATE_OFF
@property
def is_on(self):
""" True if device is on. """
return False
def turn_on(self, **kwargs):
""" Turn the device on. """
pass
def turn_off(self, **kwargs):
""" Turn the device off. """
pass

View File

@ -0,0 +1,120 @@
"""
homeassistant.helpers.device
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides ABC for devices in HA.
"""
from homeassistant import NoEntitySpecifiedError
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

View File

@ -2,13 +2,15 @@
Provides helpers for components that handle devices. Provides helpers for components that handle devices.
""" """
from homeassistant.loader import get_component from homeassistant.loader import get_component
from homeassistant.helpers import generate_entity_id, config_per_platform from homeassistant.helpers import (
generate_entity_id, config_per_platform, extract_entity_ids)
from homeassistant.components import group, discovery from homeassistant.components import group, discovery
from homeassistant.const import ATTR_ENTITY_ID
class DeviceComponent(object): class DeviceComponent(object):
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-arguments,too-few-public-methods # pylint: disable=too-many-arguments
""" """
Helper class that will help a device component manage its devices. Helper class that will help a device component manage its devices.
""" """
@ -52,6 +54,18 @@ class DeviceComponent(object):
discovery.listen(self.hass, self.discovery_platforms.keys(), discovery.listen(self.hass, self.discovery_platforms.keys(),
self._device_discovered) 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): def _update_device_states(self, now):
""" Update the states of all the lights. """ """ Update the states of all the lights. """
self.logger.info("Updating %s states", self.domain) self.logger.info("Updating %s states", self.domain)

View File

@ -1,5 +1,6 @@
# required # required for Home Assistant core
requests>=2.0 requests>=2.0
pyyaml>=3.11
# optional, needed for specific components # optional, needed for specific components
@ -12,17 +13,17 @@ pyephem>=3.7
# lights.hue # lights.hue
phue>=0.8 phue>=0.8
# chromecast # media_player.cast
pychromecast>=0.5 pychromecast>=0.5
# keyboard # keyboard
pyuserinput>=0.1.9 pyuserinput>=0.1.9
# switch.tellstick, tellstick_sensor # switch.tellstick, sensor.tellstick
tellcore-py>=1.0.4 tellcore-py>=1.0.4
# device_tracker.nmap # device_tracker.nmap
python-libnmap python-libnmap>=0.6.2
# notify.pushbullet # notify.pushbullet
pushbullet.py>=0.7.1 pushbullet.py>=0.7.1
@ -33,5 +34,5 @@ python-nest>=2.1
# z-wave # z-wave
pydispatcher>=2.0.5 pydispatcher>=2.0.5
# pyyaml # sensor.systemmonitor
pyyaml psutil>=2.2.1

View File

@ -7,7 +7,7 @@ Helper method for writing tests.
import os import os
import homeassistant as ha import homeassistant as ha
from homeassistant.helpers import ToggleDevice from homeassistant.helpers.device import ToggleDevice
from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME from homeassistant.const import STATE_ON, STATE_OFF, DEVICE_DEFAULT_NAME

View File

@ -11,7 +11,7 @@ import os
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 (
ATTR_ENTITY_ID, STATE_ON, STATE_OFF, CONF_TYPE, ATTR_ENTITY_ID, STATE_ON, STATE_OFF, CONF_PLATFORM,
SERVICE_TURN_ON, SERVICE_TURN_OFF) SERVICE_TURN_ON, SERVICE_TURN_OFF)
import homeassistant.components.light as light import homeassistant.components.light as light
@ -101,7 +101,7 @@ class TestLight(unittest.TestCase):
platform.init() platform.init()
self.assertTrue( self.assertTrue(
light.setup(self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}})) light.setup(self.hass, {light.DOMAIN: {CONF_PLATFORM: 'test'}}))
dev1, dev2, dev3 = platform.DEVICES dev1, dev2, dev3 = platform.DEVICES
@ -226,7 +226,7 @@ class TestLight(unittest.TestCase):
user_file.write('I,WILL,NOT,WORK\n') user_file.write('I,WILL,NOT,WORK\n')
self.assertFalse(light.setup( self.assertFalse(light.setup(
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}} self.hass, {light.DOMAIN: {CONF_PLATFORM: 'test'}}
)) ))
def test_light_profiles(self): def test_light_profiles(self):
@ -241,7 +241,7 @@ class TestLight(unittest.TestCase):
user_file.write('test,.4,.6,100\n') user_file.write('test,.4,.6,100\n')
self.assertTrue(light.setup( self.assertTrue(light.setup(
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}} self.hass, {light.DOMAIN: {CONF_PLATFORM: 'test'}}
)) ))
dev1, dev2, dev3 = platform.DEVICES dev1, dev2, dev3 = platform.DEVICES

View File

@ -1,8 +1,8 @@
""" """
tests.test_component_chromecast tests.test_component_media_player
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests Chromecast component. Tests media_player component.
""" """
# pylint: disable=too-many-public-methods,protected-access # pylint: disable=too-many-public-methods,protected-access
import logging import logging
@ -12,28 +12,27 @@ import homeassistant as ha
from homeassistant.const import ( from homeassistant.const import (
SERVICE_TURN_OFF, SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_TURN_OFF, SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN,
SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE,
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREV_TRACK, ATTR_ENTITY_ID, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREV_TRACK, ATTR_ENTITY_ID)
CONF_HOSTS) import homeassistant.components.media_player as media_player
import homeassistant.components.chromecast as chromecast
from helpers import mock_service from helpers import mock_service
def setUpModule(): # pylint: disable=invalid-name def setUpModule(): # pylint: disable=invalid-name
""" Setup to ignore chromecast errors. """ """ Setup to ignore media_player errors. """
logging.disable(logging.CRITICAL) logging.disable(logging.CRITICAL)
class TestChromecast(unittest.TestCase): class TestMediaPlayer(unittest.TestCase):
""" Test the chromecast module. """ """ Test the media_player module. """
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
self.hass = ha.HomeAssistant() self.hass = ha.HomeAssistant()
self.test_entity = chromecast.ENTITY_ID_FORMAT.format('living_room') self.test_entity = media_player.ENTITY_ID_FORMAT.format('living_room')
self.hass.states.set(self.test_entity, chromecast.STATE_NO_APP) self.hass.states.set(self.test_entity, media_player.STATE_NO_APP)
self.test_entity2 = chromecast.ENTITY_ID_FORMAT.format('bedroom') self.test_entity2 = media_player.ENTITY_ID_FORMAT.format('bedroom')
self.hass.states.set(self.test_entity2, "Youtube") self.hass.states.set(self.test_entity2, "YouTube")
def tearDown(self): # pylint: disable=invalid-name def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """ """ Stop down stuff we started. """
@ -41,33 +40,33 @@ class TestChromecast(unittest.TestCase):
def test_is_on(self): def test_is_on(self):
""" Test is_on method. """ """ Test is_on method. """
self.assertFalse(chromecast.is_on(self.hass, self.test_entity)) self.assertFalse(media_player.is_on(self.hass, self.test_entity))
self.assertTrue(chromecast.is_on(self.hass, self.test_entity2)) self.assertTrue(media_player.is_on(self.hass, self.test_entity2))
def test_services(self): def test_services(self):
""" """
Test if the call service methods conver to correct service calls. Test if the call service methods conver to correct service calls.
""" """
services = { services = {
SERVICE_TURN_OFF: chromecast.turn_off, SERVICE_TURN_OFF: media_player.turn_off,
SERVICE_VOLUME_UP: chromecast.volume_up, SERVICE_VOLUME_UP: media_player.volume_up,
SERVICE_VOLUME_DOWN: chromecast.volume_down, SERVICE_VOLUME_DOWN: media_player.volume_down,
SERVICE_MEDIA_PLAY_PAUSE: chromecast.media_play_pause, SERVICE_MEDIA_PLAY_PAUSE: media_player.media_play_pause,
SERVICE_MEDIA_PLAY: chromecast.media_play, SERVICE_MEDIA_PLAY: media_player.media_play,
SERVICE_MEDIA_PAUSE: chromecast.media_pause, SERVICE_MEDIA_PAUSE: media_player.media_pause,
SERVICE_MEDIA_NEXT_TRACK: chromecast.media_next_track, SERVICE_MEDIA_NEXT_TRACK: media_player.media_next_track,
SERVICE_MEDIA_PREV_TRACK: chromecast.media_prev_track SERVICE_MEDIA_PREV_TRACK: media_player.media_prev_track
} }
for service_name, service_method in services.items(): for service_name, service_method in services.items():
calls = mock_service(self.hass, chromecast.DOMAIN, service_name) calls = mock_service(self.hass, media_player.DOMAIN, service_name)
service_method(self.hass) service_method(self.hass)
self.hass.pool.block_till_done() self.hass.pool.block_till_done()
self.assertEqual(1, len(calls)) self.assertEqual(1, len(calls))
call = calls[-1] call = calls[-1]
self.assertEqual(chromecast.DOMAIN, call.domain) self.assertEqual(media_player.DOMAIN, call.domain)
self.assertEqual(service_name, call.service) self.assertEqual(service_name, call.service)
service_method(self.hass, self.test_entity) service_method(self.hass, self.test_entity)
@ -75,7 +74,7 @@ class TestChromecast(unittest.TestCase):
self.assertEqual(2, len(calls)) self.assertEqual(2, len(calls))
call = calls[-1] call = calls[-1]
self.assertEqual(chromecast.DOMAIN, call.domain) self.assertEqual(media_player.DOMAIN, call.domain)
self.assertEqual(service_name, call.service) self.assertEqual(service_name, call.service)
self.assertEqual(self.test_entity, self.assertEqual(self.test_entity,
call.data.get(ATTR_ENTITY_ID)) call.data.get(ATTR_ENTITY_ID))