mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
commit
851bff1e12
40
.coveragerc
Normal file
40
.coveragerc
Normal 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
|
@ -7,6 +7,6 @@ install:
|
||||
script:
|
||||
- flake8 homeassistant --exclude bower_components,external
|
||||
- pylint homeassistant
|
||||
- coverage run --source=homeassistant --omit "homeassistant/external/*" -m unittest discover tests
|
||||
- coverage run -m unittest discover tests
|
||||
after_success:
|
||||
- coveralls
|
||||
|
@ -102,3 +102,21 @@ automation 2:
|
||||
|
||||
execute_service: notify.notify
|
||||
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'
|
@ -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
|
@ -17,7 +17,8 @@ DOMAIN = "demo"
|
||||
|
||||
DEPENDENCIES = []
|
||||
|
||||
COMPONENTS_WITH_DEMO_PLATFORM = ['switch', 'light', 'thermostat', 'sensor']
|
||||
COMPONENTS_WITH_DEMO_PLATFORM = [
|
||||
'switch', 'light', 'thermostat', 'sensor', 'media_player']
|
||||
|
||||
|
||||
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
|
||||
configurator_ids = []
|
||||
|
||||
|
@ -16,7 +16,7 @@ import homeassistant.util as util
|
||||
|
||||
from homeassistant.const import (
|
||||
STATE_HOME, STATE_NOT_HOME, ATTR_ENTITY_PICTURE, ATTR_FRIENDLY_NAME,
|
||||
CONF_PLATFORM, CONF_TYPE)
|
||||
CONF_PLATFORM)
|
||||
from homeassistant.components import group
|
||||
|
||||
DOMAIN = "device_tracker"
|
||||
@ -53,21 +53,11 @@ def is_on(hass, entity_id=None):
|
||||
def setup(hass, config):
|
||||
""" 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) or
|
||||
validate_config(config, {DOMAIN: [CONF_TYPE]}, _LOGGER)):
|
||||
|
||||
if not validate_config(config, {DOMAIN: [CONF_PLATFORM]}, _LOGGER):
|
||||
return False
|
||||
|
||||
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(
|
||||
'device_tracker.{}'.format(tracker_type))
|
||||
|
||||
|
@ -25,7 +25,7 @@ SCAN_INTERVAL = 300 # seconds
|
||||
|
||||
SERVICE_HANDLERS = {
|
||||
services.BELKIN_WEMO: "switch",
|
||||
services.GOOGLE_CAST: "chromecast",
|
||||
services.GOOGLE_CAST: "media_player",
|
||||
services.PHILIPS_HUE: "light",
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,8 @@ from homeassistant.const import URL_ROOT, HTTP_OK
|
||||
DOMAIN = 'frontend'
|
||||
DEPENDENCIES = ['api']
|
||||
|
||||
INDEX_PATH = os.path.join(os.path.dirname(__file__), 'index.html.template')
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -40,10 +42,6 @@ def setup(hass, config):
|
||||
def _handle_get_root(handler, path_match, data):
|
||||
""" 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_header('Content-type', 'text/html; charset=utf-8')
|
||||
handler.end_headers()
|
||||
@ -54,28 +52,16 @@ def _handle_get_root(handler, path_match, data):
|
||||
app_url = "frontend-{}.html".format(version.VERSION)
|
||||
|
||||
# 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', ''))
|
||||
|
||||
write(("<!doctype html>"
|
||||
"<html>"
|
||||
"<head><title>Home Assistant</title>"
|
||||
"<meta name='mobile-web-app-capable' content='yes'>"
|
||||
"<link rel='shortcut icon' href='/static/favicon.ico' />"
|
||||
"<link rel='icon' type='image/png' "
|
||||
" href='/static/favicon-192x192.png' sizes='192x192'>"
|
||||
"<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))
|
||||
with open(INDEX_PATH) as template_file:
|
||||
template_html = template_file.read()
|
||||
|
||||
template_html = template_html.replace('{{ app_url }}', app_url)
|
||||
template_html = template_html.replace('{{ auth }}', auth)
|
||||
|
||||
handler.wfile.write(template_html.encode("UTF-8"))
|
||||
|
||||
|
||||
def _handle_get_static(handler, path_match, data):
|
||||
|
28
homeassistant/components/frontend/index.html.template
Normal file
28
homeassistant/components/frontend/index.html.template
Normal 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>
|
@ -1,2 +1,2 @@
|
||||
""" 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
14
homeassistant/components/frontend/www_static/manifest.json
Normal file
14
homeassistant/components/frontend/www_static/manifest.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
"core-icons": "polymer/core-icons#~0.5.5",
|
||||
"core-image": "polymer/core-image#~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-dialog": "Polymer/paper-dialog#~0.5.5",
|
||||
"paper-spinner": "Polymer/paper-spinner#~0.5.5",
|
||||
@ -33,6 +34,7 @@
|
||||
"paper-dropdown": "polymer/paper-dropdown#~0.5.5",
|
||||
"paper-item": "polymer/paper-item#~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",
|
||||
"google-apis": "GoogleWebComponents/google-apis#~0.4.2",
|
||||
"core-drawer-panel": "polymer/core-drawer-panel#~0.5.5",
|
||||
|
@ -29,7 +29,7 @@
|
||||
case "switch":
|
||||
return "image:flash-on";
|
||||
|
||||
case "chromecast":
|
||||
case "media_player":
|
||||
var icon = "hardware:cast";
|
||||
|
||||
if (state !== "idle") {
|
||||
@ -47,9 +47,6 @@
|
||||
case "light":
|
||||
return "image:wb-incandescent";
|
||||
|
||||
case "tellstick_sensor":
|
||||
return "trending-up";
|
||||
|
||||
case "simple_alarm":
|
||||
return "social:notifications";
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 2f96b21d42c6b1c01a235b39fbbd2e0cf1b8d651
|
||||
Subproject commit 8ea3a9e858a8c39d4c3aa46b719362b33f4a358f
|
@ -30,8 +30,9 @@
|
||||
|
||||
</template>
|
||||
<script>
|
||||
var storeListenerMixIn = window.hass.storeListenerMixIn;
|
||||
var uiActions = window.hass.uiActions;
|
||||
var storeListenerMixIn = window.hass.storeListenerMixIn,
|
||||
uiActions = window.hass.uiActions,
|
||||
preferenceStore = window.hass.preferenceStore;
|
||||
|
||||
Polymer(Polymer.mixin({
|
||||
loaded: false,
|
||||
@ -42,7 +43,9 @@
|
||||
|
||||
// if auth was given, tell the backend
|
||||
if(this.auth) {
|
||||
uiActions.validateAuth(this.auth);
|
||||
uiActions.validateAuth(this.auth, false);
|
||||
} else if (preferenceStore.hasAuthToken()) {
|
||||
uiActions.validateAuth(preferenceStore.getAuthToken(), false);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
<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-input/paper-input-decorator.html">
|
||||
<link rel="import" href="../bower_components/core-input/core-input.html">
|
||||
@ -8,15 +10,29 @@
|
||||
<polymer-element name="login-form">
|
||||
<template>
|
||||
<style>
|
||||
paper-input {
|
||||
#passwordDecorator {
|
||||
display: block;
|
||||
height: 57px;
|
||||
}
|
||||
|
||||
.login paper-button {
|
||||
margin-left: 242px;
|
||||
paper-checkbox {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -44,7 +60,14 @@
|
||||
<input is="core-input" type="password" id="passwordInput"
|
||||
value="{{authToken}}" on-keyup="{{passwordKeyup}}">
|
||||
</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 id="validatebox" hidden?="{{!(isValidating || isLoggedIn)}}">
|
||||
@ -66,6 +89,7 @@
|
||||
MSG_LOADING_DATA: "Loading data…",
|
||||
|
||||
authToken: "",
|
||||
rememberLogin: false,
|
||||
|
||||
isValidating: false,
|
||||
isLoggedIn: false,
|
||||
@ -87,7 +111,7 @@
|
||||
this.spinnerMessage = this.isValidating ? this.MSG_VALIDATING : this.MSG_LOADING_DATA;
|
||||
|
||||
if (authStore.wasLastAttemptInvalid()) {
|
||||
this.$.passwordDecorator.error = this.authStore.getLastAttemptMessage();
|
||||
this.$.passwordDecorator.error = authStore.getLastAttemptMessage();
|
||||
this.$.passwordDecorator.isInvalid = true;
|
||||
}
|
||||
|
||||
@ -114,7 +138,7 @@
|
||||
validatePassword: function() {
|
||||
this.$.hideKeyboardOnFocus.focus();
|
||||
|
||||
uiActions.validateAuth(this.authToken);
|
||||
uiActions.validateAuth(this.authToken, this.rememberLogin);
|
||||
},
|
||||
}, storeListenerMixIn));
|
||||
</script>
|
||||
|
@ -61,8 +61,11 @@
|
||||
});
|
||||
},
|
||||
|
||||
validateAuth: function(authToken) {
|
||||
authActions.validate(authToken, preferenceStore.useStreaming());
|
||||
validateAuth: function(authToken, rememberLogin) {
|
||||
authActions.validate(authToken, {
|
||||
useStreaming: preferenceStore.useStreaming(),
|
||||
rememberLogin: rememberLogin,
|
||||
});
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
@ -111,7 +111,7 @@ def setup(hass, config=None):
|
||||
if config is None or DOMAIN not in config:
|
||||
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
|
||||
|
||||
@ -123,7 +123,7 @@ def setup(hass, config=None):
|
||||
|
||||
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_host, server_port), RequestHandler, hass, api_password,
|
||||
@ -341,17 +341,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
||||
self.send_response(HTTP_OK)
|
||||
self.send_header(HTTP_HEADER_CONTENT_TYPE, content_type)
|
||||
|
||||
# Add cache if not 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))
|
||||
self.set_cache_header()
|
||||
|
||||
if do_gzip:
|
||||
gzip_data = gzip.compress(inp.read())
|
||||
@ -374,3 +364,16 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
||||
|
||||
else:
|
||||
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))
|
@ -57,7 +57,6 @@ from homeassistant.helpers.device_component import DeviceComponent
|
||||
import homeassistant.util as util
|
||||
from homeassistant.const import (
|
||||
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
|
||||
|
||||
|
||||
@ -146,8 +145,6 @@ def setup(hass, config):
|
||||
GROUP_NAME_ALL_LIGHTS)
|
||||
component.setup(config)
|
||||
|
||||
lights = component.devices
|
||||
|
||||
# Load built-in profiles and custom profiles
|
||||
profile_paths = [os.path.join(os.path.dirname(__file__),
|
||||
LIGHT_PROFILES_FILE),
|
||||
@ -182,12 +179,7 @@ def setup(hass, config):
|
||||
dat = service.data
|
||||
|
||||
# Convert the entity ids to valid light ids
|
||||
target_lights = [lights[entity_id] for entity_id
|
||||
in extract_entity_ids(hass, service)
|
||||
if entity_id in lights]
|
||||
|
||||
if not target_lights:
|
||||
target_lights = lights.values()
|
||||
target_lights = component.extract_from_service(service)
|
||||
|
||||
params = {}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
""" Provides demo lights. """
|
||||
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.components.light import ATTR_BRIGHTNESS, ATTR_XY_COLOR
|
||||
|
||||
|
@ -6,14 +6,14 @@ from urllib.parse import urlparse
|
||||
|
||||
from homeassistant.loader import get_component
|
||||
import homeassistant.util as util
|
||||
from homeassistant.helpers import ToggleDevice
|
||||
from homeassistant.helpers.device import ToggleDevice
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS, ATTR_XY_COLOR, ATTR_TRANSITION,
|
||||
ATTR_FLASH, FLASH_LONG, FLASH_SHORT)
|
||||
|
||||
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"
|
||||
|
||||
|
@ -3,7 +3,7 @@ import logging
|
||||
# pylint: disable=no-name-in-module, import-error
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS
|
||||
from homeassistant.const import ATTR_FRIENDLY_NAME
|
||||
from homeassistant.helpers import ToggleDevice
|
||||
from homeassistant.helpers.device import ToggleDevice
|
||||
import tellcore.constants as tellcore_constants
|
||||
|
||||
|
||||
|
207
homeassistant/components/media_player/__init__.py
Normal file
207
homeassistant/components/media_player/__init__.py
Normal 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
|
162
homeassistant/components/media_player/cast.py
Normal file
162
homeassistant/components/media_player/cast.py
Normal 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)
|
100
homeassistant/components/media_player/demo.py
Normal file
100
homeassistant/components/media_player/demo.py
Normal 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
|
@ -1,5 +1,5 @@
|
||||
""" Support for Wink sensors. """
|
||||
from homeassistant.helpers import Device
|
||||
from homeassistant.helpers.device import Device
|
||||
from homeassistant.const import (
|
||||
TEMP_CELCIUS, ATTR_UNIT_OF_MEASUREMENT, ATTR_FRIENDLY_NAME)
|
||||
|
||||
|
97
homeassistant/components/sensor/systemmonitor.py
Normal file
97
homeassistant/components/sensor/systemmonitor.py
Normal 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
|
126
homeassistant/components/sensor/tellstick.py
Normal file
126
homeassistant/components/sensor/tellstick.py
Normal 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
|
@ -9,7 +9,7 @@ from openzwave.network import ZWaveNetwork
|
||||
from pydispatch import dispatcher
|
||||
|
||||
import homeassistant.components.zwave as zwave
|
||||
from homeassistant.helpers import Device
|
||||
from homeassistant.helpers.device import Device
|
||||
from homeassistant.const import (
|
||||
ATTR_BATTERY_LEVEL, ATTR_UNIT_OF_MEASUREMENT, STATE_ON, STATE_OFF,
|
||||
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_LOCATION)
|
||||
|
@ -10,7 +10,6 @@ from homeassistant.helpers.device_component import DeviceComponent
|
||||
|
||||
from homeassistant.const import (
|
||||
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
|
||||
|
||||
DOMAIN = 'switch'
|
||||
@ -64,16 +63,9 @@ def setup(hass, config):
|
||||
GROUP_NAME_ALL_SWITCHES)
|
||||
component.setup(config)
|
||||
|
||||
switches = component.devices
|
||||
|
||||
def handle_switch_service(service):
|
||||
""" Handles calls to the switch services. """
|
||||
target_switches = [switches[entity_id] for entity_id
|
||||
in extract_entity_ids(hass, service)
|
||||
if entity_id in switches]
|
||||
|
||||
if not target_switches:
|
||||
target_switches = switches.values()
|
||||
target_switches = component.extract_from_service(service)
|
||||
|
||||
for switch in target_switches:
|
||||
if service.service == SERVICE_TURN_ON:
|
||||
|
@ -1,5 +1,5 @@
|
||||
""" 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
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@ import logging
|
||||
|
||||
|
||||
from homeassistant.const import ATTR_FRIENDLY_NAME
|
||||
from homeassistant.helpers import ToggleDevice
|
||||
from homeassistant.helpers.device import ToggleDevice
|
||||
import tellcore.constants as tellcore_constants
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
""" Support for WeMo switchces. """
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers import ToggleDevice
|
||||
from homeassistant.helpers.device import ToggleDevice
|
||||
from homeassistant.components.switch import (
|
||||
ATTR_TODAY_MWH, ATTR_CURRENT_POWER_MWH)
|
||||
|
||||
|
@ -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
|
@ -9,7 +9,7 @@ import logging
|
||||
from homeassistant.helpers.device_component import DeviceComponent
|
||||
|
||||
import homeassistant.util as util
|
||||
from homeassistant.helpers import Device, extract_entity_ids
|
||||
from homeassistant.helpers.device import Device
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT,
|
||||
STATE_ON, STATE_OFF)
|
||||
@ -56,18 +56,11 @@ def setup(hass, config):
|
||||
component = DeviceComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
|
||||
component.setup(config)
|
||||
|
||||
thermostats = component.devices
|
||||
|
||||
def thermostat_service(service):
|
||||
""" Handles calls to the services. """
|
||||
|
||||
# Convert the entity ids to valid light ids
|
||||
target_thermostats = [thermostats[entity_id] for entity_id
|
||||
in extract_entity_ids(hass, service)
|
||||
if entity_id in thermostats]
|
||||
|
||||
if not target_thermostats:
|
||||
target_thermostats = thermostats.values()
|
||||
target_thermostats = component.extract_from_service(service)
|
||||
|
||||
if service.service == SERVICE_SET_AWAY_MODE:
|
||||
away_mode = service.data.get(ATTR_AWAY_MODE)
|
||||
|
@ -67,7 +67,7 @@ def setup(hass, config):
|
||||
from openzwave.option import ZWaveOption
|
||||
from openzwave.network import ZWaveNetwork
|
||||
|
||||
use_debug = config[DOMAIN].get(CONF_DEBUG) == '1'
|
||||
use_debug = str(config[DOMAIN].get(CONF_DEBUG)) == '1'
|
||||
|
||||
# Setup options
|
||||
options = ZWaveOption(
|
||||
|
@ -9,9 +9,6 @@ DEVICE_DEFAULT_NAME = "Unnamed Device"
|
||||
CONF_LATITUDE = "latitude"
|
||||
CONF_LONGITUDE = "longitude"
|
||||
|
||||
# This one is deprecated. Use platform instead.
|
||||
CONF_TYPE = "type"
|
||||
|
||||
CONF_PLATFORM = "platform"
|
||||
CONF_HOST = "host"
|
||||
CONF_HOSTS = "hosts"
|
||||
|
@ -3,14 +3,14 @@ Helper methods for components within Home Assistant.
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
from homeassistant import NoEntitySpecifiedError
|
||||
|
||||
from homeassistant.loader import get_component
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, STATE_ON, STATE_OFF, CONF_PLATFORM,
|
||||
CONF_TYPE, DEVICE_DEFAULT_NAME)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
|
||||
from homeassistant.util import ensure_unique_string, slugify
|
||||
|
||||
# Deprecated 3/5/2015 - Moved to homeassistant.helpers.device
|
||||
# pylint: disable=unused-import
|
||||
from .device import Device, ToggleDevice # noqa
|
||||
|
||||
|
||||
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. """
|
||||
@ -101,15 +101,6 @@ def config_per_platform(config, domain, logger):
|
||||
|
||||
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:
|
||||
logger.warning('No platform specified for %s', config_key)
|
||||
break
|
||||
@ -118,169 +109,3 @@ def config_per_platform(config, domain, logger):
|
||||
|
||||
found += 1
|
||||
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
|
||||
|
120
homeassistant/helpers/device.py
Normal file
120
homeassistant/helpers/device.py
Normal 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
|
@ -2,13 +2,15 @@
|
||||
Provides helpers for components that handle devices.
|
||||
"""
|
||||
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.const import ATTR_ENTITY_ID
|
||||
|
||||
|
||||
class DeviceComponent(object):
|
||||
# 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.
|
||||
"""
|
||||
@ -52,6 +54,18 @@ class DeviceComponent(object):
|
||||
discovery.listen(self.hass, self.discovery_platforms.keys(),
|
||||
self._device_discovered)
|
||||
|
||||
def extract_from_service(self, service):
|
||||
"""
|
||||
Takes a service and extracts all known devices.
|
||||
Will return all if no entity IDs given in service.
|
||||
"""
|
||||
if ATTR_ENTITY_ID not in service.data:
|
||||
return self.devices.values()
|
||||
else:
|
||||
return [self.devices[entity_id] for entity_id
|
||||
in extract_entity_ids(self.hass, service)
|
||||
if entity_id in self.devices]
|
||||
|
||||
def _update_device_states(self, now):
|
||||
""" Update the states of all the lights. """
|
||||
self.logger.info("Updating %s states", self.domain)
|
||||
|
@ -1,5 +1,6 @@
|
||||
# required
|
||||
# required for Home Assistant core
|
||||
requests>=2.0
|
||||
pyyaml>=3.11
|
||||
|
||||
# optional, needed for specific components
|
||||
|
||||
@ -12,17 +13,17 @@ pyephem>=3.7
|
||||
# lights.hue
|
||||
phue>=0.8
|
||||
|
||||
# chromecast
|
||||
# media_player.cast
|
||||
pychromecast>=0.5
|
||||
|
||||
# keyboard
|
||||
pyuserinput>=0.1.9
|
||||
|
||||
# switch.tellstick, tellstick_sensor
|
||||
# switch.tellstick, sensor.tellstick
|
||||
tellcore-py>=1.0.4
|
||||
|
||||
# device_tracker.nmap
|
||||
python-libnmap
|
||||
python-libnmap>=0.6.2
|
||||
|
||||
# notify.pushbullet
|
||||
pushbullet.py>=0.7.1
|
||||
@ -33,5 +34,5 @@ python-nest>=2.1
|
||||
# z-wave
|
||||
pydispatcher>=2.0.5
|
||||
|
||||
# pyyaml
|
||||
pyyaml
|
||||
# sensor.systemmonitor
|
||||
psutil>=2.2.1
|
||||
|
@ -7,7 +7,7 @@ Helper method for writing tests.
|
||||
import os
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@ import os
|
||||
import homeassistant.loader as loader
|
||||
import homeassistant.util as util
|
||||
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)
|
||||
import homeassistant.components.light as light
|
||||
|
||||
@ -101,7 +101,7 @@ class TestLight(unittest.TestCase):
|
||||
|
||||
platform.init()
|
||||
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
|
||||
|
||||
@ -226,7 +226,7 @@ class TestLight(unittest.TestCase):
|
||||
user_file.write('I,WILL,NOT,WORK\n')
|
||||
|
||||
self.assertFalse(light.setup(
|
||||
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
||||
self.hass, {light.DOMAIN: {CONF_PLATFORM: 'test'}}
|
||||
))
|
||||
|
||||
def test_light_profiles(self):
|
||||
@ -241,7 +241,7 @@ class TestLight(unittest.TestCase):
|
||||
user_file.write('test,.4,.6,100\n')
|
||||
|
||||
self.assertTrue(light.setup(
|
||||
self.hass, {light.DOMAIN: {CONF_TYPE: 'test'}}
|
||||
self.hass, {light.DOMAIN: {CONF_PLATFORM: 'test'}}
|
||||
))
|
||||
|
||||
dev1, dev2, dev3 = platform.DEVICES
|
||||
|
@ -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
|
||||
import logging
|
||||
@ -12,28 +12,27 @@ import homeassistant as ha
|
||||
from homeassistant.const import (
|
||||
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, ATTR_ENTITY_ID,
|
||||
CONF_HOSTS)
|
||||
import homeassistant.components.chromecast as chromecast
|
||||
SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREV_TRACK, ATTR_ENTITY_ID)
|
||||
import homeassistant.components.media_player as media_player
|
||||
from helpers import mock_service
|
||||
|
||||
|
||||
def setUpModule(): # pylint: disable=invalid-name
|
||||
""" Setup to ignore chromecast errors. """
|
||||
""" Setup to ignore media_player errors. """
|
||||
logging.disable(logging.CRITICAL)
|
||||
|
||||
|
||||
class TestChromecast(unittest.TestCase):
|
||||
""" Test the chromecast module. """
|
||||
class TestMediaPlayer(unittest.TestCase):
|
||||
""" Test the media_player module. """
|
||||
|
||||
def setUp(self): # pylint: disable=invalid-name
|
||||
self.hass = ha.HomeAssistant()
|
||||
|
||||
self.test_entity = chromecast.ENTITY_ID_FORMAT.format('living_room')
|
||||
self.hass.states.set(self.test_entity, chromecast.STATE_NO_APP)
|
||||
self.test_entity = media_player.ENTITY_ID_FORMAT.format('living_room')
|
||||
self.hass.states.set(self.test_entity, media_player.STATE_NO_APP)
|
||||
|
||||
self.test_entity2 = chromecast.ENTITY_ID_FORMAT.format('bedroom')
|
||||
self.hass.states.set(self.test_entity2, "Youtube")
|
||||
self.test_entity2 = media_player.ENTITY_ID_FORMAT.format('bedroom')
|
||||
self.hass.states.set(self.test_entity2, "YouTube")
|
||||
|
||||
def tearDown(self): # pylint: disable=invalid-name
|
||||
""" Stop down stuff we started. """
|
||||
@ -41,33 +40,33 @@ class TestChromecast(unittest.TestCase):
|
||||
|
||||
def test_is_on(self):
|
||||
""" Test is_on method. """
|
||||
self.assertFalse(chromecast.is_on(self.hass, self.test_entity))
|
||||
self.assertTrue(chromecast.is_on(self.hass, self.test_entity2))
|
||||
self.assertFalse(media_player.is_on(self.hass, self.test_entity))
|
||||
self.assertTrue(media_player.is_on(self.hass, self.test_entity2))
|
||||
|
||||
def test_services(self):
|
||||
"""
|
||||
Test if the call service methods conver to correct service calls.
|
||||
"""
|
||||
services = {
|
||||
SERVICE_TURN_OFF: chromecast.turn_off,
|
||||
SERVICE_VOLUME_UP: chromecast.volume_up,
|
||||
SERVICE_VOLUME_DOWN: chromecast.volume_down,
|
||||
SERVICE_MEDIA_PLAY_PAUSE: chromecast.media_play_pause,
|
||||
SERVICE_MEDIA_PLAY: chromecast.media_play,
|
||||
SERVICE_MEDIA_PAUSE: chromecast.media_pause,
|
||||
SERVICE_MEDIA_NEXT_TRACK: chromecast.media_next_track,
|
||||
SERVICE_MEDIA_PREV_TRACK: chromecast.media_prev_track
|
||||
SERVICE_TURN_OFF: media_player.turn_off,
|
||||
SERVICE_VOLUME_UP: media_player.volume_up,
|
||||
SERVICE_VOLUME_DOWN: media_player.volume_down,
|
||||
SERVICE_MEDIA_PLAY_PAUSE: media_player.media_play_pause,
|
||||
SERVICE_MEDIA_PLAY: media_player.media_play,
|
||||
SERVICE_MEDIA_PAUSE: media_player.media_pause,
|
||||
SERVICE_MEDIA_NEXT_TRACK: media_player.media_next_track,
|
||||
SERVICE_MEDIA_PREV_TRACK: media_player.media_prev_track
|
||||
}
|
||||
|
||||
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)
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
self.assertEqual(1, len(calls))
|
||||
call = calls[-1]
|
||||
self.assertEqual(chromecast.DOMAIN, call.domain)
|
||||
self.assertEqual(media_player.DOMAIN, call.domain)
|
||||
self.assertEqual(service_name, call.service)
|
||||
|
||||
service_method(self.hass, self.test_entity)
|
||||
@ -75,7 +74,7 @@ class TestChromecast(unittest.TestCase):
|
||||
|
||||
self.assertEqual(2, len(calls))
|
||||
call = calls[-1]
|
||||
self.assertEqual(chromecast.DOMAIN, call.domain)
|
||||
self.assertEqual(media_player.DOMAIN, call.domain)
|
||||
self.assertEqual(service_name, call.service)
|
||||
self.assertEqual(self.test_entity,
|
||||
call.data.get(ATTR_ENTITY_ID))
|
Loading…
x
Reference in New Issue
Block a user