Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
cd00ff8b56
26 changed files with 435 additions and 276 deletions
|
@ -1,7 +1,10 @@
|
|||
""" Starts home assistant. """
|
||||
from __future__ import print_function
|
||||
|
||||
from multiprocessing import Process
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
import os
|
||||
import argparse
|
||||
|
||||
|
@ -204,6 +207,64 @@ def uninstall_osx():
|
|||
print("Home Assistant has been uninstalled.")
|
||||
|
||||
|
||||
def setup_and_run_hass(config_dir, args):
|
||||
""" Setup HASS and run. Block until stopped. """
|
||||
if args.demo_mode:
|
||||
config = {
|
||||
'frontend': {},
|
||||
'demo': {}
|
||||
}
|
||||
hass = bootstrap.from_config_dict(
|
||||
config, config_dir=config_dir, daemon=args.daemon,
|
||||
verbose=args.verbose, skip_pip=args.skip_pip,
|
||||
log_rotate_days=args.log_rotate_days)
|
||||
else:
|
||||
config_file = ensure_config_file(config_dir)
|
||||
print('Config directory:', config_dir)
|
||||
hass = bootstrap.from_config_file(
|
||||
config_file, daemon=args.daemon, verbose=args.verbose,
|
||||
skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days)
|
||||
|
||||
if args.open_ui:
|
||||
def open_browser(event):
|
||||
""" Open the webinterface in a browser. """
|
||||
if hass.config.api is not None:
|
||||
import webbrowser
|
||||
webbrowser.open(hass.config.api.base_url)
|
||||
|
||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, open_browser)
|
||||
|
||||
hass.start()
|
||||
sys.exit(int(hass.block_till_stopped()))
|
||||
|
||||
|
||||
def run_hass_process(hass_proc):
|
||||
""" Runs a child hass process. Returns True if it should be restarted. """
|
||||
requested_stop = threading.Event()
|
||||
hass_proc.daemon = True
|
||||
|
||||
def request_stop():
|
||||
""" request hass stop """
|
||||
requested_stop.set()
|
||||
hass_proc.terminate()
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGTERM, request_stop)
|
||||
except ValueError:
|
||||
print('Could not bind to SIGTERM. Are you running in a thread?')
|
||||
|
||||
hass_proc.start()
|
||||
try:
|
||||
hass_proc.join()
|
||||
except KeyboardInterrupt:
|
||||
request_stop()
|
||||
try:
|
||||
hass_proc.join()
|
||||
except KeyboardInterrupt:
|
||||
return False
|
||||
return not requested_stop.isSet() and hass_proc.exitcode == 100
|
||||
|
||||
|
||||
def main():
|
||||
""" Starts Home Assistant. """
|
||||
validate_python()
|
||||
|
@ -233,33 +294,12 @@ def main():
|
|||
if args.pid_file:
|
||||
write_pid(args.pid_file)
|
||||
|
||||
if args.demo_mode:
|
||||
config = {
|
||||
'frontend': {},
|
||||
'demo': {}
|
||||
}
|
||||
hass = bootstrap.from_config_dict(
|
||||
config, config_dir=config_dir, daemon=args.daemon,
|
||||
verbose=args.verbose, skip_pip=args.skip_pip,
|
||||
log_rotate_days=args.log_rotate_days)
|
||||
else:
|
||||
config_file = ensure_config_file(config_dir)
|
||||
print('Config directory:', config_dir)
|
||||
hass = bootstrap.from_config_file(
|
||||
config_file, daemon=args.daemon, verbose=args.verbose,
|
||||
skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days)
|
||||
# Run hass as child process. Restart if necessary.
|
||||
keep_running = True
|
||||
while keep_running:
|
||||
hass_proc = Process(target=setup_and_run_hass, args=(config_dir, args))
|
||||
keep_running = run_hass_process(hass_proc)
|
||||
|
||||
if args.open_ui:
|
||||
def open_browser(event):
|
||||
""" Open the webinterface in a browser. """
|
||||
if hass.config.api is not None:
|
||||
import webbrowser
|
||||
webbrowser.open(hass.config.api.base_url)
|
||||
|
||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, open_browser)
|
||||
|
||||
hass.start()
|
||||
hass.block_till_stopped()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -16,7 +16,7 @@ from homeassistant.components.light import \
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
REQUIREMENTS = ['liffylights==0.9.3']
|
||||
REQUIREMENTS = ['liffylights==0.9.4']
|
||||
DEPENDENCIES = []
|
||||
|
||||
CONF_SERVER = "server" # server address configuration item
|
||||
|
@ -64,6 +64,12 @@ class LIFX():
|
|||
power, hue, sat, bri, kel)
|
||||
self._devices.append(bulb)
|
||||
self._add_devices_callback([bulb])
|
||||
else:
|
||||
_LOGGER.debug("update bulb %s %s %d %d %d %d %d",
|
||||
ipaddr, name, power, hue, sat, bri, kel)
|
||||
bulb.set_power(power)
|
||||
bulb.set_color(hue, sat, bri, kel)
|
||||
bulb.update_ha_state()
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def on_color(self, ipaddr, hue, sat, bri, kel):
|
||||
|
@ -97,7 +103,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
|||
lifx_library = LIFX(add_devices_callback, server_addr, broadcast_addr)
|
||||
|
||||
# register our poll service
|
||||
track_time_change(hass, lifx_library.poll, second=10)
|
||||
track_time_change(hass, lifx_library.poll, second=[10, 40])
|
||||
|
||||
lifx_library.probe()
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ DISCOVERY_PLATFORMS = {
|
|||
discovery.SERVICE_PLEX: 'plex',
|
||||
}
|
||||
|
||||
SERVICE_YOUTUBE_VIDEO = 'play_youtube_video'
|
||||
SERVICE_PLAY_MEDIA = 'play_media'
|
||||
|
||||
ATTR_MEDIA_VOLUME_LEVEL = 'volume_level'
|
||||
|
@ -68,14 +67,12 @@ SUPPORT_VOLUME_SET = 4
|
|||
SUPPORT_VOLUME_MUTE = 8
|
||||
SUPPORT_PREVIOUS_TRACK = 16
|
||||
SUPPORT_NEXT_TRACK = 32
|
||||
SUPPORT_YOUTUBE = 64
|
||||
|
||||
SUPPORT_TURN_ON = 128
|
||||
SUPPORT_TURN_OFF = 256
|
||||
SUPPORT_PLAY_MEDIA = 512
|
||||
SUPPORT_VOLUME_STEP = 1024
|
||||
|
||||
YOUTUBE_COVER_URL_FORMAT = 'https://img.youtube.com/vi/{}/1.jpg'
|
||||
|
||||
SERVICE_TO_METHOD = {
|
||||
SERVICE_TURN_ON: 'turn_on',
|
||||
SERVICE_TURN_OFF: 'turn_off',
|
||||
|
@ -200,6 +197,13 @@ def media_previous_track(hass, entity_id=None):
|
|||
hass.services.call(DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK, data)
|
||||
|
||||
|
||||
def media_seek(hass, position, entity_id=None):
|
||||
""" Send the media player the command to seek in current playing media. """
|
||||
data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
|
||||
data[ATTR_MEDIA_SEEK_POSITION] = position
|
||||
hass.services.call(DOMAIN, SERVICE_MEDIA_SEEK, data)
|
||||
|
||||
|
||||
def play_media(hass, media_type, media_id, entity_id=None):
|
||||
""" Send the media player the command for playing media. """
|
||||
data = {"media_type": media_type, "media_id": media_id}
|
||||
|
@ -283,7 +287,7 @@ def setup(hass, config):
|
|||
position = service.data[ATTR_MEDIA_SEEK_POSITION]
|
||||
|
||||
for player in target_players:
|
||||
player.seek(position)
|
||||
player.media_seek(position)
|
||||
|
||||
if player.should_poll:
|
||||
player.update_ha_state(True)
|
||||
|
@ -291,20 +295,6 @@ def setup(hass, config):
|
|||
hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service,
|
||||
descriptions.get(SERVICE_MEDIA_SEEK))
|
||||
|
||||
def play_youtube_video_service(service, media_id=None):
|
||||
""" Plays specified media_id on the media player. """
|
||||
if media_id is None:
|
||||
service.data.get('video')
|
||||
|
||||
if media_id is None:
|
||||
return
|
||||
|
||||
for player in component.extract_from_service(service):
|
||||
player.play_youtube(media_id)
|
||||
|
||||
if player.should_poll:
|
||||
player.update_ha_state(True)
|
||||
|
||||
def play_media_service(service):
|
||||
""" Plays specified media_id on the media player. """
|
||||
media_type = service.data.get('media_type')
|
||||
|
@ -322,20 +312,6 @@ def setup(hass, config):
|
|||
if player.should_poll:
|
||||
player.update_ha_state(True)
|
||||
|
||||
hass.services.register(
|
||||
DOMAIN, "start_fireplace",
|
||||
lambda service: play_youtube_video_service(service, "eyU3bRy2x44"),
|
||||
descriptions.get('start_fireplace'))
|
||||
|
||||
hass.services.register(
|
||||
DOMAIN, "start_epic_sax",
|
||||
lambda service: play_youtube_video_service(service, "kxopViU98Xo"),
|
||||
descriptions.get('start_epic_sax'))
|
||||
|
||||
hass.services.register(
|
||||
DOMAIN, SERVICE_YOUTUBE_VIDEO, play_youtube_video_service,
|
||||
descriptions.get(SERVICE_YOUTUBE_VIDEO))
|
||||
|
||||
hass.services.register(
|
||||
DOMAIN, SERVICE_PLAY_MEDIA, play_media_service,
|
||||
descriptions.get(SERVICE_PLAY_MEDIA))
|
||||
|
@ -490,10 +466,6 @@ class MediaPlayerDevice(Entity):
|
|||
""" Send seek command. """
|
||||
raise NotImplementedError()
|
||||
|
||||
def play_youtube(self, media_id):
|
||||
""" Plays a YouTube media. """
|
||||
raise NotImplementedError()
|
||||
|
||||
def play_media(self, media_type, media_id):
|
||||
""" Plays a piece of media. """
|
||||
raise NotImplementedError()
|
||||
|
@ -529,11 +501,6 @@ class MediaPlayerDevice(Entity):
|
|||
""" Boolean if next track command supported. """
|
||||
return bool(self.supported_media_commands & SUPPORT_NEXT_TRACK)
|
||||
|
||||
@property
|
||||
def support_youtube(self):
|
||||
""" Boolean if YouTube is supported. """
|
||||
return bool(self.supported_media_commands & SUPPORT_YOUTUBE)
|
||||
|
||||
@property
|
||||
def support_play_media(self):
|
||||
""" Boolean if play media command supported. """
|
||||
|
|
|
@ -16,7 +16,7 @@ from homeassistant.const import (
|
|||
from homeassistant.components.media_player import (
|
||||
MediaPlayerDevice,
|
||||
SUPPORT_PAUSE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE,
|
||||
SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_YOUTUBE, SUPPORT_PLAY_MEDIA,
|
||||
SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_PLAY_MEDIA,
|
||||
SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK,
|
||||
MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO)
|
||||
|
||||
|
@ -25,51 +25,48 @@ CONF_IGNORE_CEC = 'ignore_cec'
|
|||
CAST_SPLASH = 'https://home-assistant.io/images/cast/splash.png'
|
||||
SUPPORT_CAST = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
||||
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PREVIOUS_TRACK | \
|
||||
SUPPORT_NEXT_TRACK | SUPPORT_YOUTUBE | SUPPORT_PLAY_MEDIA
|
||||
SUPPORT_NEXT_TRACK | SUPPORT_PLAY_MEDIA
|
||||
KNOWN_HOSTS = []
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
cast = None
|
||||
DEFAULT_PORT = 8009
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the cast platform. """
|
||||
global cast
|
||||
import pychromecast
|
||||
cast = pychromecast
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# import CEC IGNORE attributes
|
||||
ignore_cec = config.get(CONF_IGNORE_CEC, [])
|
||||
if isinstance(ignore_cec, list):
|
||||
cast.IGNORE_CEC += ignore_cec
|
||||
pychromecast.IGNORE_CEC += ignore_cec
|
||||
else:
|
||||
logger.error('Chromecast conig, %s must be a list.', CONF_IGNORE_CEC)
|
||||
logger.error('CEC config "%s" must be a list.', CONF_IGNORE_CEC)
|
||||
|
||||
hosts = []
|
||||
|
||||
if discovery_info and discovery_info[0] not in KNOWN_HOSTS:
|
||||
hosts = [discovery_info[0]]
|
||||
if discovery_info and discovery_info in KNOWN_HOSTS:
|
||||
return
|
||||
|
||||
elif discovery_info:
|
||||
hosts = [discovery_info]
|
||||
|
||||
elif CONF_HOST in config:
|
||||
hosts = [config[CONF_HOST]]
|
||||
hosts = [(config[CONF_HOST], DEFAULT_PORT)]
|
||||
|
||||
else:
|
||||
hosts = (host_port[0] for host_port
|
||||
in cast.discover_chromecasts()
|
||||
if host_port[0] not in KNOWN_HOSTS)
|
||||
hosts = [host for host in pychromecast.discover_chromecasts()
|
||||
if host not in KNOWN_HOSTS]
|
||||
|
||||
casts = []
|
||||
|
||||
for host in hosts:
|
||||
try:
|
||||
casts.append(CastDevice(host))
|
||||
except cast.ChromecastConnectionError:
|
||||
pass
|
||||
else:
|
||||
casts.append(CastDevice(*host))
|
||||
KNOWN_HOSTS.append(host)
|
||||
except pychromecast.ChromecastConnectionError:
|
||||
pass
|
||||
|
||||
add_devices(casts)
|
||||
|
||||
|
@ -80,11 +77,9 @@ class CastDevice(MediaPlayerDevice):
|
|||
# pylint: disable=abstract-method
|
||||
# pylint: disable=too-many-public-methods
|
||||
|
||||
def __init__(self, host):
|
||||
import pychromecast.controllers.youtube as youtube
|
||||
self.cast = cast.Chromecast(host)
|
||||
self.youtube = youtube.YouTubeController()
|
||||
self.cast.register_handler(self.youtube)
|
||||
def __init__(self, host, port):
|
||||
import pychromecast
|
||||
self.cast = pychromecast.Chromecast(host, port)
|
||||
|
||||
self.cast.socket_client.receiver_controller.register_status_listener(
|
||||
self)
|
||||
|
@ -224,11 +219,13 @@ class CastDevice(MediaPlayerDevice):
|
|||
""" Turns on the ChromeCast. """
|
||||
# The only way we can turn the Chromecast is on is by launching an app
|
||||
if not self.cast.status or not self.cast.status.is_active_input:
|
||||
import pychromecast
|
||||
|
||||
if self.cast.app_id:
|
||||
self.cast.quit_app()
|
||||
|
||||
self.cast.play_media(
|
||||
CAST_SPLASH, cast.STREAM_TYPE_BUFFERED)
|
||||
CAST_SPLASH, pychromecast.STREAM_TYPE_BUFFERED)
|
||||
|
||||
def turn_off(self):
|
||||
""" Turns Chromecast off. """
|
||||
|
@ -266,10 +263,6 @@ class CastDevice(MediaPlayerDevice):
|
|||
""" Plays media from a URL """
|
||||
self.cast.media_controller.play_media(media_id, media_type)
|
||||
|
||||
def play_youtube(self, media_id):
|
||||
""" Plays a YouTube media. """
|
||||
self.youtube.play_video(media_id)
|
||||
|
||||
# implementation of chromecast status_listener methods
|
||||
|
||||
def new_cast_status(self, status):
|
||||
|
|
|
@ -7,11 +7,11 @@ from homeassistant.const import (
|
|||
STATE_PLAYING, STATE_PAUSED, STATE_OFF)
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
MediaPlayerDevice, YOUTUBE_COVER_URL_FORMAT,
|
||||
MediaPlayerDevice,
|
||||
MEDIA_TYPE_VIDEO, MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW,
|
||||
SUPPORT_PAUSE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE, SUPPORT_YOUTUBE,
|
||||
SUPPORT_PAUSE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE,
|
||||
SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_PREVIOUS_TRACK,
|
||||
SUPPORT_NEXT_TRACK)
|
||||
SUPPORT_NEXT_TRACK, SUPPORT_PLAY_MEDIA)
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
@ -26,9 +26,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
])
|
||||
|
||||
|
||||
YOUTUBE_COVER_URL_FORMAT = 'https://img.youtube.com/vi/{}/1.jpg'
|
||||
|
||||
YOUTUBE_PLAYER_SUPPORT = \
|
||||
SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
||||
SUPPORT_YOUTUBE | SUPPORT_TURN_ON | SUPPORT_TURN_OFF
|
||||
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PLAY_MEDIA
|
||||
|
||||
MUSIC_PLAYER_SUPPORT = \
|
||||
SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
||||
|
@ -150,10 +152,9 @@ class DemoYoutubePlayer(AbstractDemoPlayer):
|
|||
""" Flags of media commands that are supported. """
|
||||
return YOUTUBE_PLAYER_SUPPORT
|
||||
|
||||
def play_youtube(self, media_id):
|
||||
""" Plays a YouTube media. """
|
||||
def play_media(self, media_type, media_id):
|
||||
""" Plays a piece of media. """
|
||||
self.youtube_id = media_id
|
||||
self._media_title = 'some YouTube video'
|
||||
self.update_ha_state()
|
||||
|
||||
|
||||
|
@ -234,7 +235,7 @@ class DemoMusicPlayer(AbstractDemoPlayer):
|
|||
""" Flags of media commands that are supported. """
|
||||
support = MUSIC_PLAYER_SUPPORT
|
||||
|
||||
if self._cur_track > 1:
|
||||
if self._cur_track > 0:
|
||||
support |= SUPPORT_PREVIOUS_TRACK
|
||||
|
||||
if self._cur_track < len(self.tracks)-1:
|
||||
|
|
|
@ -24,7 +24,6 @@ SUPPORT_DENON = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
|||
SUPPORT_TURN_ON | SUPPORT_TURN_OFF
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the Denon platform. """
|
||||
if not config.get(CONF_HOST):
|
||||
|
@ -48,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
class DenonDevice(MediaPlayerDevice):
|
||||
""" Represents a Denon device. """
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
# pylint: disable=too-many-public-methods, abstract-method
|
||||
|
||||
def __init__(self, name, host):
|
||||
self._name = name
|
||||
|
@ -145,10 +144,6 @@ class DenonDevice(MediaPlayerDevice):
|
|||
""" mute (true) or unmute (false) media player. """
|
||||
self.telnet_command("MU" + ("ON" if mute else "OFF"))
|
||||
|
||||
def media_play_pause(self):
|
||||
""" media_play_pause media player. """
|
||||
raise NotImplementedError()
|
||||
|
||||
def media_play(self):
|
||||
""" media_play media player. """
|
||||
self.telnet_command("NS9A")
|
||||
|
@ -164,9 +159,6 @@ class DenonDevice(MediaPlayerDevice):
|
|||
def media_previous_track(self):
|
||||
self.telnet_command("NS9E")
|
||||
|
||||
def media_seek(self, position):
|
||||
raise NotImplementedError()
|
||||
|
||||
def turn_on(self):
|
||||
""" turn the media player on. """
|
||||
self.telnet_command("PWON")
|
||||
|
|
|
@ -105,6 +105,8 @@ class FireTV(object):
|
|||
class FireTVDevice(MediaPlayerDevice):
|
||||
""" Represents an Amazon Fire TV device on the network. """
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
|
||||
def __init__(self, host, device, name):
|
||||
self._firetv = FireTV(host, device)
|
||||
self._name = name
|
||||
|
@ -176,15 +178,3 @@ class FireTVDevice(MediaPlayerDevice):
|
|||
def media_next_track(self):
|
||||
""" Send next track command (results in fast-forward). """
|
||||
self._firetv.action('media_next')
|
||||
|
||||
def media_seek(self, position):
|
||||
raise NotImplementedError()
|
||||
|
||||
def mute_volume(self, mute):
|
||||
raise NotImplementedError()
|
||||
|
||||
def play_youtube(self, media_id):
|
||||
raise NotImplementedError()
|
||||
|
||||
def set_volume_level(self, volume):
|
||||
raise NotImplementedError()
|
||||
|
|
|
@ -22,7 +22,6 @@ SUPPORT_KODI = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
|||
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the kodi platform. """
|
||||
|
||||
|
@ -47,7 +46,7 @@ def _get_image_url(kodi_url):
|
|||
class KodiDevice(MediaPlayerDevice):
|
||||
""" Represents a XBMC/Kodi device. """
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
# pylint: disable=too-many-public-methods, abstract-method
|
||||
|
||||
def __init__(self, name, url, auth=None):
|
||||
import jsonrpc_requests
|
||||
|
@ -263,11 +262,3 @@ class KodiDevice(MediaPlayerDevice):
|
|||
self._server.Player.Seek(players[0]['playerid'], time)
|
||||
|
||||
self.update_ha_state()
|
||||
|
||||
def turn_on(self):
|
||||
""" turn the media player on. """
|
||||
raise NotImplementedError()
|
||||
|
||||
def play_youtube(self, media_id):
|
||||
""" Plays a YouTube media. """
|
||||
raise NotImplementedError()
|
||||
|
|
|
@ -59,7 +59,7 @@ def config_from_file(filename, config=None):
|
|||
return {}
|
||||
|
||||
|
||||
# pylint: disable=abstract-method, unused-argument
|
||||
# pylint: disable=abstract-method
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Sets up the plex platform. """
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ SUPPORT_SQUEEZEBOX = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | \
|
|||
SUPPORT_SEEK | SUPPORT_TURN_ON | SUPPORT_TURN_OFF
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the squeezebox platform. """
|
||||
if not config.get(CONF_HOST):
|
||||
|
@ -138,7 +137,7 @@ class LogitechMediaServer(object):
|
|||
class SqueezeBoxDevice(MediaPlayerDevice):
|
||||
""" Represents a SqueezeBox device. """
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
# pylint: disable=too-many-arguments, abstract-method
|
||||
def __init__(self, lms, player_id):
|
||||
super(SqueezeBoxDevice, self).__init__()
|
||||
self._lms = lms
|
||||
|
@ -292,7 +291,3 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
|||
""" turn the media player on. """
|
||||
self._lms.query(self._id, 'power', '1')
|
||||
self.update_ha_state()
|
||||
|
||||
def play_youtube(self, media_id):
|
||||
""" Plays a YouTube media. """
|
||||
raise NotImplementedError()
|
||||
|
|
|
@ -27,7 +27,7 @@ from homeassistant.components.media_player import (
|
|||
MediaPlayerDevice, DOMAIN,
|
||||
SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE,
|
||||
SUPPORT_TURN_ON, SUPPORT_TURN_OFF,
|
||||
SERVICE_PLAY_MEDIA, SERVICE_YOUTUBE_VIDEO,
|
||||
SERVICE_PLAY_MEDIA,
|
||||
ATTR_SUPPORTED_MEDIA_COMMANDS, ATTR_MEDIA_VOLUME_MUTED,
|
||||
ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_DURATION,
|
||||
ATTR_MEDIA_TITLE, ATTR_MEDIA_ARTIST, ATTR_MEDIA_ALBUM_NAME,
|
||||
|
@ -397,11 +397,6 @@ class UniversalMediaPlayer(MediaPlayerDevice):
|
|||
data = {ATTR_MEDIA_SEEK_POSITION: position}
|
||||
self._call_service(SERVICE_MEDIA_SEEK, data)
|
||||
|
||||
def play_youtube(self, media_id):
|
||||
""" Plays a YouTube media. """
|
||||
data = {'media_id': media_id}
|
||||
self._call_service(SERVICE_YOUTUBE_VIDEO, data)
|
||||
|
||||
def play_media(self, media_type, media_id):
|
||||
""" Plays a piece of media. """
|
||||
data = {'media_type': media_type, 'media_id': media_id}
|
||||
|
|
|
@ -96,5 +96,7 @@ class OneWire(Entity):
|
|||
equals_pos = lines[1].find('t=')
|
||||
if equals_pos != -1:
|
||||
temp_string = lines[1][equals_pos+2:]
|
||||
temp = float(temp_string) / 1000.0
|
||||
temp = round(float(temp_string) / 1000.0, 1)
|
||||
if temp < -55 or temp > 125:
|
||||
return
|
||||
self._state = temp
|
||||
|
|
|
@ -11,7 +11,9 @@ import logging
|
|||
|
||||
from datetime import datetime
|
||||
|
||||
from homeassistant.const import TEMP_CELCIUS, ATTR_BATTERY_LEVEL
|
||||
from homeassistant.const import (TEMP_CELCIUS,
|
||||
ATTR_BATTERY_LEVEL,
|
||||
DEVICE_DEFAULT_NAME)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components import tellduslive
|
||||
|
||||
|
@ -64,7 +66,8 @@ class TelldusLiveSensor(Entity):
|
|||
self._sensor_id = sensor_id
|
||||
self._sensor_type = sensor_type
|
||||
self._state = None
|
||||
self._name = sensor_name + ' ' + SENSOR_TYPES[sensor_type][0]
|
||||
self._name = "{} {}".format(sensor_name or DEVICE_DEFAULT_NAME,
|
||||
SENSOR_TYPES[sensor_type][0])
|
||||
self._last_update = None
|
||||
self._battery_level = None
|
||||
self.update()
|
||||
|
|
|
@ -9,16 +9,20 @@ https://home-assistant.io/components/sensor.template/
|
|||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity import Entity, generate_entity_id
|
||||
from homeassistant.core import EVENT_STATE_CHANGED
|
||||
from homeassistant.const import (
|
||||
ATTR_FRIENDLY_NAME,
|
||||
CONF_VALUE_TEMPLATE,
|
||||
ATTR_UNIT_OF_MEASUREMENT)
|
||||
|
||||
from homeassistant.util import template
|
||||
from homeassistant.util import template, slugify
|
||||
from homeassistant.exceptions import TemplateError
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
CONF_SENSORS = 'sensors'
|
||||
STATE_ERROR = 'error'
|
||||
|
@ -34,9 +38,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
return False
|
||||
|
||||
for device, device_config in config[CONF_SENSORS].items():
|
||||
|
||||
if device != slugify(device):
|
||||
_LOGGER.error("Found invalid key for sensor.template: %s. "
|
||||
"Use %s instead", device, slugify(device))
|
||||
continue
|
||||
|
||||
if not isinstance(device_config, dict):
|
||||
_LOGGER.error("Missing configuration data for sensor %s", device)
|
||||
continue
|
||||
|
||||
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
|
||||
unit_of_measurement = device_config.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
state_template = device_config.get(CONF_VALUE_TEMPLATE)
|
||||
|
@ -44,14 +55,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||
_LOGGER.error(
|
||||
"Missing %s for sensor %s", CONF_VALUE_TEMPLATE, device)
|
||||
continue
|
||||
|
||||
sensors.append(
|
||||
SensorTemplate(
|
||||
hass,
|
||||
device,
|
||||
friendly_name,
|
||||
unit_of_measurement,
|
||||
state_template)
|
||||
)
|
||||
if sensors is None:
|
||||
if not sensors:
|
||||
_LOGGER.error("No sensors added")
|
||||
return False
|
||||
add_devices(sensors)
|
||||
|
@ -64,10 +77,15 @@ class SensorTemplate(Entity):
|
|||
# pylint: disable=too-many-arguments
|
||||
def __init__(self,
|
||||
hass,
|
||||
device_id,
|
||||
friendly_name,
|
||||
unit_of_measurement,
|
||||
state_template):
|
||||
|
||||
self.entity_id = generate_entity_id(
|
||||
ENTITY_ID_FORMAT, device_id,
|
||||
hass=hass)
|
||||
|
||||
self.hass = hass
|
||||
self._name = friendly_name
|
||||
self._unit_of_measurement = unit_of_measurement
|
||||
|
|
|
@ -28,7 +28,7 @@ SENSOR_TYPES = {
|
|||
'temperature': ['Temperature', '°C'],
|
||||
'windSpeed': ['Wind speed', 'm/s'],
|
||||
'windGust': ['Wind gust', 'm/s'],
|
||||
'pressure': ['Pressure', 'mbar'],
|
||||
'pressure': ['Pressure', 'hPa'],
|
||||
'windDirection': ['Wind direction', '°'],
|
||||
'humidity': ['Humidity', '%'],
|
||||
'fog': ['Fog', '%'],
|
||||
|
|
|
@ -126,6 +126,7 @@ ATTR_GPS_ACCURACY = 'gps_accuracy'
|
|||
|
||||
# #### SERVICES ####
|
||||
SERVICE_HOMEASSISTANT_STOP = "stop"
|
||||
SERVICE_HOMEASSISTANT_RESTART = "restart"
|
||||
|
||||
SERVICE_TURN_ON = 'turn_on'
|
||||
SERVICE_TURN_OFF = 'turn_off'
|
||||
|
|
|
@ -16,7 +16,8 @@ from collections import namedtuple
|
|||
|
||||
from homeassistant.const import (
|
||||
__version__, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
|
||||
SERVICE_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, EVENT_STATE_CHANGED,
|
||||
SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART,
|
||||
EVENT_TIME_CHANGED, EVENT_STATE_CHANGED,
|
||||
EVENT_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL,
|
||||
EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED,
|
||||
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_FRIENDLY_NAME, ATTR_SERVICE_DATA)
|
||||
|
@ -47,6 +48,9 @@ _LOGGER = logging.getLogger(__name__)
|
|||
# Temporary to support deprecated methods
|
||||
_MockHA = namedtuple("MockHomeAssistant", ['bus'])
|
||||
|
||||
# The exit code to send to request a restart
|
||||
RESTART_EXIT_CODE = 100
|
||||
|
||||
|
||||
class HomeAssistant(object):
|
||||
"""Root object of the Home Assistant home automation."""
|
||||
|
@ -70,28 +74,33 @@ class HomeAssistant(object):
|
|||
def block_till_stopped(self):
|
||||
"""Register service homeassistant/stop and will block until called."""
|
||||
request_shutdown = threading.Event()
|
||||
request_restart = threading.Event()
|
||||
|
||||
def stop_homeassistant(*args):
|
||||
"""Stop Home Assistant."""
|
||||
request_shutdown.set()
|
||||
|
||||
def restart_homeassistant(*args):
|
||||
"""Reset Home Assistant."""
|
||||
request_restart.set()
|
||||
request_shutdown.set()
|
||||
|
||||
self.services.register(
|
||||
DOMAIN, SERVICE_HOMEASSISTANT_STOP, stop_homeassistant)
|
||||
self.services.register(
|
||||
DOMAIN, SERVICE_HOMEASSISTANT_RESTART, restart_homeassistant)
|
||||
|
||||
if os.name != "nt":
|
||||
try:
|
||||
signal.signal(signal.SIGTERM, stop_homeassistant)
|
||||
except ValueError:
|
||||
_LOGGER.warning(
|
||||
'Could not bind to SIGQUIT. Are you running in a thread?')
|
||||
try:
|
||||
signal.signal(signal.SIGTERM, stop_homeassistant)
|
||||
except ValueError:
|
||||
_LOGGER.warning(
|
||||
'Could not bind to SIGTERM. Are you running in a thread?')
|
||||
|
||||
while not request_shutdown.isSet():
|
||||
try:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
self.stop()
|
||||
return RESTART_EXIT_CODE if request_restart.isSet() else 0
|
||||
|
||||
def stop(self):
|
||||
"""Stop Home Assistant and shuts down all threads."""
|
||||
|
|
|
@ -19,7 +19,7 @@ def load_yaml(fname):
|
|||
with open(fname, encoding='utf-8') as conf_file:
|
||||
# If configuration file is empty YAML returns None
|
||||
# We convert that to an empty dict
|
||||
return yaml.load(conf_file) or {}
|
||||
return yaml.safe_load(conf_file) or {}
|
||||
except yaml.YAMLError:
|
||||
error = 'Error reading YAML configuration file {}'.format(fname)
|
||||
_LOGGER.exception(error)
|
||||
|
@ -45,6 +45,6 @@ def _ordered_dict(loader, node):
|
|||
return OrderedDict(loader.construct_pairs(node))
|
||||
|
||||
|
||||
yaml.add_constructor('!include', _include_yaml)
|
||||
yaml.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
|
||||
_ordered_dict)
|
||||
yaml.SafeLoader.add_constructor('!include', _include_yaml)
|
||||
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
|
||||
_ordered_dict)
|
||||
|
|
|
@ -100,7 +100,7 @@ insteon_hub==0.4.5
|
|||
jsonrpc-requests==0.1
|
||||
|
||||
# homeassistant.components.light.lifx
|
||||
liffylights==0.9.3
|
||||
liffylights==0.9.4
|
||||
|
||||
# homeassistant.components.light.limitlessled
|
||||
limitlessled==1.0.0
|
||||
|
|
30
tests/components/media_player/test_cast.py
Normal file
30
tests/components/media_player/test_cast.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
"""
|
||||
tests.component.media_player.test_cast
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Tests cast media_player component.
|
||||
"""
|
||||
# pylint: disable=too-many-public-methods,protected-access
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.media_player import cast
|
||||
|
||||
|
||||
class TestCastMediaPlayer(unittest.TestCase):
|
||||
""" Test the media_player module. """
|
||||
|
||||
@patch('homeassistant.components.media_player.cast.CastDevice')
|
||||
def test_filter_duplicates(self, mock_device):
|
||||
cast.setup_platform(None, {
|
||||
'host': 'some_host'
|
||||
}, lambda _: _)
|
||||
|
||||
assert mock_device.called
|
||||
|
||||
mock_device.reset_mock()
|
||||
assert not mock_device.called
|
||||
|
||||
cast.setup_platform(None, {}, lambda _: _, ('some_host',
|
||||
cast.DEFAULT_PORT))
|
||||
assert not mock_device.called
|
141
tests/components/media_player/test_demo.py
Normal file
141
tests/components/media_player/test_demo.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
"""
|
||||
tests.component.media_player.test_demo
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Tests demo media_player component.
|
||||
"""
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from pprint import pprint
|
||||
import homeassistant.core as ha
|
||||
from homeassistant.const import (
|
||||
STATE_OFF, STATE_ON, STATE_UNKNOWN, STATE_PLAYING, STATE_PAUSED)
|
||||
import homeassistant.components.media_player as mp
|
||||
|
||||
|
||||
entity_id = 'media_player.walkman'
|
||||
|
||||
|
||||
class TestDemoMediaPlayer(unittest.TestCase):
|
||||
""" Test the media_player module. """
|
||||
|
||||
def setUp(self): # pylint: disable=invalid-name
|
||||
self.hass = ha.HomeAssistant()
|
||||
|
||||
def tearDown(self): # pylint: disable=invalid-name
|
||||
""" Stop down stuff we started. """
|
||||
self.hass.stop()
|
||||
|
||||
def test_volume_services(self):
|
||||
assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
|
||||
state = self.hass.states.get(entity_id)
|
||||
assert 1.0 == state.attributes.get('volume_level')
|
||||
|
||||
mp.set_volume_level(self.hass, 0.5, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
state = self.hass.states.get(entity_id)
|
||||
assert 0.5 == state.attributes.get('volume_level')
|
||||
|
||||
mp.volume_down(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
state = self.hass.states.get(entity_id)
|
||||
assert 0.4 == state.attributes.get('volume_level')
|
||||
|
||||
mp.volume_up(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
state = self.hass.states.get(entity_id)
|
||||
assert 0.5 == state.attributes.get('volume_level')
|
||||
|
||||
assert False is state.attributes.get('is_volume_muted')
|
||||
mp.mute_volume(self.hass, True, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
state = self.hass.states.get(entity_id)
|
||||
assert True is state.attributes.get('is_volume_muted')
|
||||
|
||||
def test_turning_off_and_on(self):
|
||||
assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
|
||||
assert self.hass.states.is_state(entity_id, 'playing')
|
||||
|
||||
mp.turn_off(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
assert self.hass.states.is_state(entity_id, 'off')
|
||||
assert not mp.is_on(self.hass, entity_id)
|
||||
|
||||
mp.turn_on(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
assert self.hass.states.is_state(entity_id, 'playing')
|
||||
|
||||
mp.toggle(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
assert self.hass.states.is_state(entity_id, 'off')
|
||||
assert not mp.is_on(self.hass, entity_id)
|
||||
|
||||
def test_playing_pausing(self):
|
||||
assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
|
||||
assert self.hass.states.is_state(entity_id, 'playing')
|
||||
|
||||
mp.media_pause(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
assert self.hass.states.is_state(entity_id, 'paused')
|
||||
|
||||
mp.media_play_pause(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
assert self.hass.states.is_state(entity_id, 'playing')
|
||||
|
||||
mp.media_play_pause(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
assert self.hass.states.is_state(entity_id, 'paused')
|
||||
|
||||
mp.media_play(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
assert self.hass.states.is_state(entity_id, 'playing')
|
||||
|
||||
def test_prev_next_track(self):
|
||||
assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
|
||||
state = self.hass.states.get(entity_id)
|
||||
assert 1 == state.attributes.get('media_track')
|
||||
assert 0 == (mp.SUPPORT_PREVIOUS_TRACK &
|
||||
state.attributes.get('supported_media_commands'))
|
||||
|
||||
mp.media_next_track(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
state = self.hass.states.get(entity_id)
|
||||
assert 2 == state.attributes.get('media_track')
|
||||
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
|
||||
state.attributes.get('supported_media_commands'))
|
||||
|
||||
mp.media_next_track(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
state = self.hass.states.get(entity_id)
|
||||
assert 3 == state.attributes.get('media_track')
|
||||
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
|
||||
state.attributes.get('supported_media_commands'))
|
||||
|
||||
mp.media_previous_track(self.hass, entity_id)
|
||||
self.hass.pool.block_till_done()
|
||||
state = self.hass.states.get(entity_id)
|
||||
assert 2 == state.attributes.get('media_track')
|
||||
assert 0 < (mp.SUPPORT_PREVIOUS_TRACK &
|
||||
state.attributes.get('supported_media_commands'))
|
||||
|
||||
@patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.media_seek')
|
||||
def test_play_media(self, mock_seek):
|
||||
assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}})
|
||||
ent_id = 'media_player.living_room'
|
||||
state = self.hass.states.get(ent_id)
|
||||
assert 0 < (mp.SUPPORT_PLAY_MEDIA &
|
||||
state.attributes.get('supported_media_commands'))
|
||||
assert state.attributes.get('media_content_id') is not None
|
||||
|
||||
mp.play_media(self.hass, 'youtube', 'some_id', ent_id)
|
||||
self.hass.pool.block_till_done()
|
||||
state = self.hass.states.get(ent_id)
|
||||
assert 0 < (mp.SUPPORT_PLAY_MEDIA &
|
||||
state.attributes.get('supported_media_commands'))
|
||||
assert 'some_id' == state.attributes.get('media_content_id')
|
||||
|
||||
assert not mock_seek.called
|
||||
mp.media_seek(self.hass, 100, ent_id)
|
||||
self.hass.pool.block_till_done()
|
||||
assert mock_seek.called
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
"""
|
||||
tests.test_component_media_player
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Tests media_player component.
|
||||
"""
|
||||
# pylint: disable=too-many-public-methods,protected-access
|
||||
import unittest
|
||||
|
||||
import homeassistant.core as ha
|
||||
from homeassistant.const import (
|
||||
STATE_OFF,
|
||||
SERVICE_TURN_ON, 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_PREVIOUS_TRACK, SERVICE_TOGGLE,
|
||||
ATTR_ENTITY_ID)
|
||||
import homeassistant.components.media_player as media_player
|
||||
from tests.common import mock_service
|
||||
|
||||
|
||||
class TestMediaPlayer(unittest.TestCase):
|
||||
""" Test the media_player module. """
|
||||
|
||||
def setUp(self): # pylint: disable=invalid-name
|
||||
self.hass = ha.HomeAssistant()
|
||||
|
||||
self.test_entity = media_player.ENTITY_ID_FORMAT.format('living_room')
|
||||
self.hass.states.set(self.test_entity, STATE_OFF)
|
||||
|
||||
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. """
|
||||
self.hass.stop()
|
||||
|
||||
def test_is_on(self):
|
||||
""" Test is_on method. """
|
||||
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 convert to correct service calls.
|
||||
"""
|
||||
services = {
|
||||
SERVICE_TURN_ON: media_player.turn_on,
|
||||
SERVICE_TURN_OFF: media_player.turn_off,
|
||||
SERVICE_TOGGLE: media_player.toggle,
|
||||
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_PREVIOUS_TRACK: media_player.media_previous_track
|
||||
}
|
||||
|
||||
for service_name, service_method in services.items():
|
||||
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(media_player.DOMAIN, call.domain)
|
||||
self.assertEqual(service_name, call.service)
|
||||
|
||||
service_method(self.hass, self.test_entity)
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
self.assertEqual(2, len(calls))
|
||||
call = calls[-1]
|
||||
self.assertEqual(media_player.DOMAIN, call.domain)
|
||||
self.assertEqual(service_name, call.service)
|
||||
self.assertEqual(self.test_entity,
|
||||
call.data.get(ATTR_ENTITY_ID))
|
|
@ -4,9 +4,6 @@ tests.components.sensor.template
|
|||
|
||||
Tests template sensor.
|
||||
"""
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
import homeassistant.core as ha
|
||||
import homeassistant.components.sensor as sensor
|
||||
|
@ -56,8 +53,54 @@ class TestTemplateSensor:
|
|||
}
|
||||
})
|
||||
|
||||
|
||||
self.hass.states.set('sensor.test_state', 'Works')
|
||||
self.hass.pool.block_till_done()
|
||||
state = self.hass.states.get('sensor.test_template_sensor')
|
||||
assert state.state == 'error'
|
||||
|
||||
def test_invalid_name_does_not_create(self):
|
||||
assert sensor.setup(self.hass, {
|
||||
'sensor': {
|
||||
'platform': 'template',
|
||||
'sensors': {
|
||||
'test INVALID sensor': {
|
||||
'value_template':
|
||||
"{{ states.sensor.test_state.state }}"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
assert self.hass.states.all() == []
|
||||
|
||||
def test_invalid_sensor_does_not_create(self):
|
||||
assert sensor.setup(self.hass, {
|
||||
'sensor': {
|
||||
'platform': 'template',
|
||||
'sensors': {
|
||||
'test_template_sensor': 'invalid'
|
||||
}
|
||||
}
|
||||
})
|
||||
assert self.hass.states.all() == []
|
||||
|
||||
def test_no_sensors_does_not_create(self):
|
||||
assert sensor.setup(self.hass, {
|
||||
'sensor': {
|
||||
'platform': 'template'
|
||||
}
|
||||
})
|
||||
assert self.hass.states.all() == []
|
||||
|
||||
def test_missing_template_does_not_create(self):
|
||||
assert sensor.setup(self.hass, {
|
||||
'sensor': {
|
||||
'platform': 'template',
|
||||
'sensors': {
|
||||
'test_template_sensor': {
|
||||
'not_value_template':
|
||||
"{{ states.sensor.test_state.state }}"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
assert self.hass.states.all() == []
|
||||
|
|
|
@ -43,37 +43,47 @@ class TestSensorYr:
|
|||
|
||||
state = self.hass.states.get('sensor.yr_symbol')
|
||||
|
||||
assert '46' == state.state
|
||||
assert state.state.isnumeric()
|
||||
assert state.attributes.get('unit_of_measurement') is None
|
||||
|
||||
def test_custom_setup(self, betamax_session):
|
||||
now = datetime(2016, 1, 5, 1, tzinfo=dt_util.UTC)
|
||||
|
||||
with patch('homeassistant.components.sensor.yr.requests.Session',
|
||||
return_value=betamax_session):
|
||||
assert sensor.setup(self.hass, {
|
||||
'sensor': {
|
||||
'platform': 'yr',
|
||||
'elevation': 0,
|
||||
'monitored_conditions': {
|
||||
'pressure',
|
||||
'windDirection',
|
||||
'humidity',
|
||||
'fog',
|
||||
'windSpeed'
|
||||
with patch('homeassistant.components.sensor.yr.dt_util.utcnow',
|
||||
return_value=now):
|
||||
assert sensor.setup(self.hass, {
|
||||
'sensor': {
|
||||
'platform': 'yr',
|
||||
'elevation': 0,
|
||||
'monitored_conditions': {
|
||||
'pressure',
|
||||
'windDirection',
|
||||
'humidity',
|
||||
'fog',
|
||||
'windSpeed'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
state = self.hass.states.get('sensor.yr_pressure')
|
||||
assert 'hPa', state.attributes.get('unit_of_measurement')
|
||||
assert 'hPa' == state.attributes.get('unit_of_measurement')
|
||||
assert '1025.1' == state.state
|
||||
|
||||
state = self.hass.states.get('sensor.yr_wind_direction')
|
||||
assert '°', state.attributes.get('unit_of_measurement')
|
||||
assert '°'== state.attributes.get('unit_of_measurement')
|
||||
assert '81.8' == state.state
|
||||
|
||||
state = self.hass.states.get('sensor.yr_humidity')
|
||||
assert '%', state.attributes.get('unit_of_measurement')
|
||||
assert '%' == state.attributes.get('unit_of_measurement')
|
||||
assert '79.6' == state.state
|
||||
|
||||
state = self.hass.states.get('sensor.yr_fog')
|
||||
assert '%', state.attributes.get('unit_of_measurement')
|
||||
assert '%' == state.attributes.get('unit_of_measurement')
|
||||
assert '0.0' == state.state
|
||||
|
||||
state = self.hass.states.get('sensor.yr_wind_speed')
|
||||
assert 'm/s', state.attributes.get('unit_of_measurement')
|
||||
assert '4.3' == state.state
|
||||
|
|
|
@ -94,6 +94,15 @@ class TestConfig(unittest.TestCase):
|
|||
with self.assertRaises(HomeAssistantError):
|
||||
config_util.load_yaml_config_file(YAML_PATH)
|
||||
|
||||
def test_load_yaml_config_raises_error_if_unsafe_yaml(self):
|
||||
""" Test error raised if unsafe YAML. """
|
||||
with open(YAML_PATH, 'w') as f:
|
||||
f.write('hello: !!python/object/apply:os.system')
|
||||
|
||||
with self.assertRaises(HomeAssistantError):
|
||||
config_util.load_yaml_config_file(YAML_PATH)
|
||||
|
||||
|
||||
def test_load_yaml_config_preserves_key_order(self):
|
||||
with open(YAML_PATH, 'w') as f:
|
||||
f.write('hello: 0\n')
|
||||
|
|
|
@ -7,6 +7,7 @@ Provides tests to verify that Home Assistant core works.
|
|||
# pylint: disable=protected-access,too-many-public-methods
|
||||
# pylint: disable=too-few-public-methods
|
||||
import os
|
||||
import signal
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
import time
|
||||
|
@ -79,15 +80,15 @@ class TestHomeAssistant(unittest.TestCase):
|
|||
|
||||
self.assertFalse(blocking_thread.is_alive())
|
||||
|
||||
def test_stopping_with_keyboardinterrupt(self):
|
||||
def test_stopping_with_sigterm(self):
|
||||
calls = []
|
||||
self.hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
|
||||
lambda event: calls.append(1))
|
||||
|
||||
def raise_keyboardinterrupt(length):
|
||||
raise KeyboardInterrupt
|
||||
def send_sigterm(length):
|
||||
os.kill(os.getpid(), signal.SIGTERM)
|
||||
|
||||
with patch('homeassistant.core.time.sleep', raise_keyboardinterrupt):
|
||||
with patch('homeassistant.core.time.sleep', send_sigterm):
|
||||
self.hass.block_till_stopped()
|
||||
|
||||
self.assertEqual(1, len(calls))
|
||||
|
|
Loading…
Add table
Reference in a new issue