mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
cd00ff8b56
@ -1,7 +1,10 @@
|
|||||||
""" Starts home assistant. """
|
""" Starts home assistant. """
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
from multiprocessing import Process
|
||||||
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
import os
|
import os
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
@ -204,6 +207,64 @@ def uninstall_osx():
|
|||||||
print("Home Assistant has been uninstalled.")
|
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():
|
def main():
|
||||||
""" Starts Home Assistant. """
|
""" Starts Home Assistant. """
|
||||||
validate_python()
|
validate_python()
|
||||||
@ -233,33 +294,12 @@ def main():
|
|||||||
if args.pid_file:
|
if args.pid_file:
|
||||||
write_pid(args.pid_file)
|
write_pid(args.pid_file)
|
||||||
|
|
||||||
if args.demo_mode:
|
# Run hass as child process. Restart if necessary.
|
||||||
config = {
|
keep_running = True
|
||||||
'frontend': {},
|
while keep_running:
|
||||||
'demo': {}
|
hass_proc = Process(target=setup_and_run_hass, args=(config_dir, args))
|
||||||
}
|
keep_running = run_hass_process(hass_proc)
|
||||||
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()
|
|
||||||
hass.block_till_stopped()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
@ -16,7 +16,7 @@ from homeassistant.components.light import \
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
REQUIREMENTS = ['liffylights==0.9.3']
|
REQUIREMENTS = ['liffylights==0.9.4']
|
||||||
DEPENDENCIES = []
|
DEPENDENCIES = []
|
||||||
|
|
||||||
CONF_SERVER = "server" # server address configuration item
|
CONF_SERVER = "server" # server address configuration item
|
||||||
@ -64,6 +64,12 @@ class LIFX():
|
|||||||
power, hue, sat, bri, kel)
|
power, hue, sat, bri, kel)
|
||||||
self._devices.append(bulb)
|
self._devices.append(bulb)
|
||||||
self._add_devices_callback([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
|
# pylint: disable=too-many-arguments
|
||||||
def on_color(self, ipaddr, hue, sat, bri, kel):
|
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)
|
lifx_library = LIFX(add_devices_callback, server_addr, broadcast_addr)
|
||||||
|
|
||||||
# register our poll service
|
# 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()
|
lifx_library.probe()
|
||||||
|
|
||||||
|
@ -32,7 +32,6 @@ DISCOVERY_PLATFORMS = {
|
|||||||
discovery.SERVICE_PLEX: 'plex',
|
discovery.SERVICE_PLEX: 'plex',
|
||||||
}
|
}
|
||||||
|
|
||||||
SERVICE_YOUTUBE_VIDEO = 'play_youtube_video'
|
|
||||||
SERVICE_PLAY_MEDIA = 'play_media'
|
SERVICE_PLAY_MEDIA = 'play_media'
|
||||||
|
|
||||||
ATTR_MEDIA_VOLUME_LEVEL = 'volume_level'
|
ATTR_MEDIA_VOLUME_LEVEL = 'volume_level'
|
||||||
@ -68,14 +67,12 @@ SUPPORT_VOLUME_SET = 4
|
|||||||
SUPPORT_VOLUME_MUTE = 8
|
SUPPORT_VOLUME_MUTE = 8
|
||||||
SUPPORT_PREVIOUS_TRACK = 16
|
SUPPORT_PREVIOUS_TRACK = 16
|
||||||
SUPPORT_NEXT_TRACK = 32
|
SUPPORT_NEXT_TRACK = 32
|
||||||
SUPPORT_YOUTUBE = 64
|
|
||||||
SUPPORT_TURN_ON = 128
|
SUPPORT_TURN_ON = 128
|
||||||
SUPPORT_TURN_OFF = 256
|
SUPPORT_TURN_OFF = 256
|
||||||
SUPPORT_PLAY_MEDIA = 512
|
SUPPORT_PLAY_MEDIA = 512
|
||||||
SUPPORT_VOLUME_STEP = 1024
|
SUPPORT_VOLUME_STEP = 1024
|
||||||
|
|
||||||
YOUTUBE_COVER_URL_FORMAT = 'https://img.youtube.com/vi/{}/1.jpg'
|
|
||||||
|
|
||||||
SERVICE_TO_METHOD = {
|
SERVICE_TO_METHOD = {
|
||||||
SERVICE_TURN_ON: 'turn_on',
|
SERVICE_TURN_ON: 'turn_on',
|
||||||
SERVICE_TURN_OFF: 'turn_off',
|
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)
|
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):
|
def play_media(hass, media_type, media_id, entity_id=None):
|
||||||
""" Send the media player the command for playing media. """
|
""" Send the media player the command for playing media. """
|
||||||
data = {"media_type": media_type, "media_id": media_id}
|
data = {"media_type": media_type, "media_id": media_id}
|
||||||
@ -283,7 +287,7 @@ def setup(hass, config):
|
|||||||
position = service.data[ATTR_MEDIA_SEEK_POSITION]
|
position = service.data[ATTR_MEDIA_SEEK_POSITION]
|
||||||
|
|
||||||
for player in target_players:
|
for player in target_players:
|
||||||
player.seek(position)
|
player.media_seek(position)
|
||||||
|
|
||||||
if player.should_poll:
|
if player.should_poll:
|
||||||
player.update_ha_state(True)
|
player.update_ha_state(True)
|
||||||
@ -291,20 +295,6 @@ def setup(hass, config):
|
|||||||
hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service,
|
hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service,
|
||||||
descriptions.get(SERVICE_MEDIA_SEEK))
|
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):
|
def play_media_service(service):
|
||||||
""" Plays specified media_id on the media player. """
|
""" Plays specified media_id on the media player. """
|
||||||
media_type = service.data.get('media_type')
|
media_type = service.data.get('media_type')
|
||||||
@ -322,20 +312,6 @@ def setup(hass, config):
|
|||||||
if player.should_poll:
|
if player.should_poll:
|
||||||
player.update_ha_state(True)
|
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(
|
hass.services.register(
|
||||||
DOMAIN, SERVICE_PLAY_MEDIA, play_media_service,
|
DOMAIN, SERVICE_PLAY_MEDIA, play_media_service,
|
||||||
descriptions.get(SERVICE_PLAY_MEDIA))
|
descriptions.get(SERVICE_PLAY_MEDIA))
|
||||||
@ -490,10 +466,6 @@ class MediaPlayerDevice(Entity):
|
|||||||
""" Send seek command. """
|
""" Send seek command. """
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def play_youtube(self, media_id):
|
|
||||||
""" Plays a YouTube media. """
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def play_media(self, media_type, media_id):
|
def play_media(self, media_type, media_id):
|
||||||
""" Plays a piece of media. """
|
""" Plays a piece of media. """
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@ -529,11 +501,6 @@ class MediaPlayerDevice(Entity):
|
|||||||
""" Boolean if next track command supported. """
|
""" Boolean if next track command supported. """
|
||||||
return bool(self.supported_media_commands & SUPPORT_NEXT_TRACK)
|
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
|
@property
|
||||||
def support_play_media(self):
|
def support_play_media(self):
|
||||||
""" Boolean if play media command supported. """
|
""" Boolean if play media command supported. """
|
||||||
|
@ -16,7 +16,7 @@ from homeassistant.const import (
|
|||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
MediaPlayerDevice,
|
MediaPlayerDevice,
|
||||||
SUPPORT_PAUSE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE,
|
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,
|
SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK,
|
||||||
MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO)
|
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'
|
CAST_SPLASH = 'https://home-assistant.io/images/cast/splash.png'
|
||||||
SUPPORT_CAST = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
SUPPORT_CAST = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
||||||
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PREVIOUS_TRACK | \
|
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 = []
|
KNOWN_HOSTS = []
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
DEFAULT_PORT = 8009
|
||||||
cast = None
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the cast platform. """
|
""" Sets up the cast platform. """
|
||||||
global cast
|
|
||||||
import pychromecast
|
import pychromecast
|
||||||
cast = pychromecast
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# import CEC IGNORE attributes
|
# import CEC IGNORE attributes
|
||||||
ignore_cec = config.get(CONF_IGNORE_CEC, [])
|
ignore_cec = config.get(CONF_IGNORE_CEC, [])
|
||||||
if isinstance(ignore_cec, list):
|
if isinstance(ignore_cec, list):
|
||||||
cast.IGNORE_CEC += ignore_cec
|
pychromecast.IGNORE_CEC += ignore_cec
|
||||||
else:
|
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 = []
|
hosts = []
|
||||||
|
|
||||||
if discovery_info and discovery_info[0] not in KNOWN_HOSTS:
|
if discovery_info and discovery_info in KNOWN_HOSTS:
|
||||||
hosts = [discovery_info[0]]
|
return
|
||||||
|
|
||||||
|
elif discovery_info:
|
||||||
|
hosts = [discovery_info]
|
||||||
|
|
||||||
elif CONF_HOST in config:
|
elif CONF_HOST in config:
|
||||||
hosts = [config[CONF_HOST]]
|
hosts = [(config[CONF_HOST], DEFAULT_PORT)]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
hosts = (host_port[0] for host_port
|
hosts = [host for host in pychromecast.discover_chromecasts()
|
||||||
in cast.discover_chromecasts()
|
if host not in KNOWN_HOSTS]
|
||||||
if host_port[0] not in KNOWN_HOSTS)
|
|
||||||
|
|
||||||
casts = []
|
casts = []
|
||||||
|
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
try:
|
try:
|
||||||
casts.append(CastDevice(host))
|
casts.append(CastDevice(*host))
|
||||||
except cast.ChromecastConnectionError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
KNOWN_HOSTS.append(host)
|
KNOWN_HOSTS.append(host)
|
||||||
|
except pychromecast.ChromecastConnectionError:
|
||||||
|
pass
|
||||||
|
|
||||||
add_devices(casts)
|
add_devices(casts)
|
||||||
|
|
||||||
@ -80,11 +77,9 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
# pylint: disable=abstract-method
|
# pylint: disable=abstract-method
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods
|
||||||
|
|
||||||
def __init__(self, host):
|
def __init__(self, host, port):
|
||||||
import pychromecast.controllers.youtube as youtube
|
import pychromecast
|
||||||
self.cast = cast.Chromecast(host)
|
self.cast = pychromecast.Chromecast(host, port)
|
||||||
self.youtube = youtube.YouTubeController()
|
|
||||||
self.cast.register_handler(self.youtube)
|
|
||||||
|
|
||||||
self.cast.socket_client.receiver_controller.register_status_listener(
|
self.cast.socket_client.receiver_controller.register_status_listener(
|
||||||
self)
|
self)
|
||||||
@ -224,11 +219,13 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
""" Turns on the ChromeCast. """
|
""" Turns on the ChromeCast. """
|
||||||
# The only way we can turn the Chromecast is on is by launching an app
|
# 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:
|
if not self.cast.status or not self.cast.status.is_active_input:
|
||||||
|
import pychromecast
|
||||||
|
|
||||||
if self.cast.app_id:
|
if self.cast.app_id:
|
||||||
self.cast.quit_app()
|
self.cast.quit_app()
|
||||||
|
|
||||||
self.cast.play_media(
|
self.cast.play_media(
|
||||||
CAST_SPLASH, cast.STREAM_TYPE_BUFFERED)
|
CAST_SPLASH, pychromecast.STREAM_TYPE_BUFFERED)
|
||||||
|
|
||||||
def turn_off(self):
|
def turn_off(self):
|
||||||
""" Turns Chromecast off. """
|
""" Turns Chromecast off. """
|
||||||
@ -266,10 +263,6 @@ class CastDevice(MediaPlayerDevice):
|
|||||||
""" Plays media from a URL """
|
""" Plays media from a URL """
|
||||||
self.cast.media_controller.play_media(media_id, media_type)
|
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
|
# implementation of chromecast status_listener methods
|
||||||
|
|
||||||
def new_cast_status(self, status):
|
def new_cast_status(self, status):
|
||||||
|
@ -7,11 +7,11 @@ from homeassistant.const import (
|
|||||||
STATE_PLAYING, STATE_PAUSED, STATE_OFF)
|
STATE_PLAYING, STATE_PAUSED, STATE_OFF)
|
||||||
|
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
MediaPlayerDevice, YOUTUBE_COVER_URL_FORMAT,
|
MediaPlayerDevice,
|
||||||
MEDIA_TYPE_VIDEO, MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW,
|
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_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_PREVIOUS_TRACK,
|
||||||
SUPPORT_NEXT_TRACK)
|
SUPPORT_NEXT_TRACK, SUPPORT_PLAY_MEDIA)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# 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 = \
|
YOUTUBE_PLAYER_SUPPORT = \
|
||||||
SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
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 = \
|
MUSIC_PLAYER_SUPPORT = \
|
||||||
SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
||||||
@ -150,10 +152,9 @@ class DemoYoutubePlayer(AbstractDemoPlayer):
|
|||||||
""" Flags of media commands that are supported. """
|
""" Flags of media commands that are supported. """
|
||||||
return YOUTUBE_PLAYER_SUPPORT
|
return YOUTUBE_PLAYER_SUPPORT
|
||||||
|
|
||||||
def play_youtube(self, media_id):
|
def play_media(self, media_type, media_id):
|
||||||
""" Plays a YouTube media. """
|
""" Plays a piece of media. """
|
||||||
self.youtube_id = media_id
|
self.youtube_id = media_id
|
||||||
self._media_title = 'some YouTube video'
|
|
||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
|
|
||||||
|
|
||||||
@ -234,7 +235,7 @@ class DemoMusicPlayer(AbstractDemoPlayer):
|
|||||||
""" Flags of media commands that are supported. """
|
""" Flags of media commands that are supported. """
|
||||||
support = MUSIC_PLAYER_SUPPORT
|
support = MUSIC_PLAYER_SUPPORT
|
||||||
|
|
||||||
if self._cur_track > 1:
|
if self._cur_track > 0:
|
||||||
support |= SUPPORT_PREVIOUS_TRACK
|
support |= SUPPORT_PREVIOUS_TRACK
|
||||||
|
|
||||||
if self._cur_track < len(self.tracks)-1:
|
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
|
SUPPORT_TURN_ON | SUPPORT_TURN_OFF
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the Denon platform. """
|
""" Sets up the Denon platform. """
|
||||||
if not config.get(CONF_HOST):
|
if not config.get(CONF_HOST):
|
||||||
@ -48,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
class DenonDevice(MediaPlayerDevice):
|
class DenonDevice(MediaPlayerDevice):
|
||||||
""" Represents a Denon device. """
|
""" Represents a Denon device. """
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods, abstract-method
|
||||||
|
|
||||||
def __init__(self, name, host):
|
def __init__(self, name, host):
|
||||||
self._name = name
|
self._name = name
|
||||||
@ -145,10 +144,6 @@ class DenonDevice(MediaPlayerDevice):
|
|||||||
""" mute (true) or unmute (false) media player. """
|
""" mute (true) or unmute (false) media player. """
|
||||||
self.telnet_command("MU" + ("ON" if mute else "OFF"))
|
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):
|
def media_play(self):
|
||||||
""" media_play media player. """
|
""" media_play media player. """
|
||||||
self.telnet_command("NS9A")
|
self.telnet_command("NS9A")
|
||||||
@ -164,9 +159,6 @@ class DenonDevice(MediaPlayerDevice):
|
|||||||
def media_previous_track(self):
|
def media_previous_track(self):
|
||||||
self.telnet_command("NS9E")
|
self.telnet_command("NS9E")
|
||||||
|
|
||||||
def media_seek(self, position):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def turn_on(self):
|
def turn_on(self):
|
||||||
""" turn the media player on. """
|
""" turn the media player on. """
|
||||||
self.telnet_command("PWON")
|
self.telnet_command("PWON")
|
||||||
|
@ -105,6 +105,8 @@ class FireTV(object):
|
|||||||
class FireTVDevice(MediaPlayerDevice):
|
class FireTVDevice(MediaPlayerDevice):
|
||||||
""" Represents an Amazon Fire TV device on the network. """
|
""" Represents an Amazon Fire TV device on the network. """
|
||||||
|
|
||||||
|
# pylint: disable=abstract-method
|
||||||
|
|
||||||
def __init__(self, host, device, name):
|
def __init__(self, host, device, name):
|
||||||
self._firetv = FireTV(host, device)
|
self._firetv = FireTV(host, device)
|
||||||
self._name = name
|
self._name = name
|
||||||
@ -176,15 +178,3 @@ class FireTVDevice(MediaPlayerDevice):
|
|||||||
def media_next_track(self):
|
def media_next_track(self):
|
||||||
""" Send next track command (results in fast-forward). """
|
""" Send next track command (results in fast-forward). """
|
||||||
self._firetv.action('media_next')
|
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
|
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the kodi platform. """
|
""" Sets up the kodi platform. """
|
||||||
|
|
||||||
@ -47,7 +46,7 @@ def _get_image_url(kodi_url):
|
|||||||
class KodiDevice(MediaPlayerDevice):
|
class KodiDevice(MediaPlayerDevice):
|
||||||
""" Represents a XBMC/Kodi device. """
|
""" 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):
|
def __init__(self, name, url, auth=None):
|
||||||
import jsonrpc_requests
|
import jsonrpc_requests
|
||||||
@ -263,11 +262,3 @@ class KodiDevice(MediaPlayerDevice):
|
|||||||
self._server.Player.Seek(players[0]['playerid'], time)
|
self._server.Player.Seek(players[0]['playerid'], time)
|
||||||
|
|
||||||
self.update_ha_state()
|
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 {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=abstract-method, unused-argument
|
# pylint: disable=abstract-method
|
||||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||||
""" Sets up the plex platform. """
|
""" Sets up the plex platform. """
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ SUPPORT_SQUEEZEBOX = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | \
|
|||||||
SUPPORT_SEEK | SUPPORT_TURN_ON | SUPPORT_TURN_OFF
|
SUPPORT_SEEK | SUPPORT_TURN_ON | SUPPORT_TURN_OFF
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
""" Sets up the squeezebox platform. """
|
""" Sets up the squeezebox platform. """
|
||||||
if not config.get(CONF_HOST):
|
if not config.get(CONF_HOST):
|
||||||
@ -138,7 +137,7 @@ class LogitechMediaServer(object):
|
|||||||
class SqueezeBoxDevice(MediaPlayerDevice):
|
class SqueezeBoxDevice(MediaPlayerDevice):
|
||||||
""" Represents a SqueezeBox device. """
|
""" Represents a SqueezeBox device. """
|
||||||
|
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments, abstract-method
|
||||||
def __init__(self, lms, player_id):
|
def __init__(self, lms, player_id):
|
||||||
super(SqueezeBoxDevice, self).__init__()
|
super(SqueezeBoxDevice, self).__init__()
|
||||||
self._lms = lms
|
self._lms = lms
|
||||||
@ -292,7 +291,3 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
|||||||
""" turn the media player on. """
|
""" turn the media player on. """
|
||||||
self._lms.query(self._id, 'power', '1')
|
self._lms.query(self._id, 'power', '1')
|
||||||
self.update_ha_state()
|
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,
|
MediaPlayerDevice, DOMAIN,
|
||||||
SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE,
|
SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE,
|
||||||
SUPPORT_TURN_ON, SUPPORT_TURN_OFF,
|
SUPPORT_TURN_ON, SUPPORT_TURN_OFF,
|
||||||
SERVICE_PLAY_MEDIA, SERVICE_YOUTUBE_VIDEO,
|
SERVICE_PLAY_MEDIA,
|
||||||
ATTR_SUPPORTED_MEDIA_COMMANDS, ATTR_MEDIA_VOLUME_MUTED,
|
ATTR_SUPPORTED_MEDIA_COMMANDS, ATTR_MEDIA_VOLUME_MUTED,
|
||||||
ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_DURATION,
|
ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_DURATION,
|
||||||
ATTR_MEDIA_TITLE, ATTR_MEDIA_ARTIST, ATTR_MEDIA_ALBUM_NAME,
|
ATTR_MEDIA_TITLE, ATTR_MEDIA_ARTIST, ATTR_MEDIA_ALBUM_NAME,
|
||||||
@ -397,11 +397,6 @@ class UniversalMediaPlayer(MediaPlayerDevice):
|
|||||||
data = {ATTR_MEDIA_SEEK_POSITION: position}
|
data = {ATTR_MEDIA_SEEK_POSITION: position}
|
||||||
self._call_service(SERVICE_MEDIA_SEEK, data)
|
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):
|
def play_media(self, media_type, media_id):
|
||||||
""" Plays a piece of media. """
|
""" Plays a piece of media. """
|
||||||
data = {'media_type': media_type, 'media_id': media_id}
|
data = {'media_type': media_type, 'media_id': media_id}
|
||||||
|
@ -96,5 +96,7 @@ class OneWire(Entity):
|
|||||||
equals_pos = lines[1].find('t=')
|
equals_pos = lines[1].find('t=')
|
||||||
if equals_pos != -1:
|
if equals_pos != -1:
|
||||||
temp_string = lines[1][equals_pos+2:]
|
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
|
self._state = temp
|
||||||
|
@ -11,7 +11,9 @@ import logging
|
|||||||
|
|
||||||
from datetime import datetime
|
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.helpers.entity import Entity
|
||||||
from homeassistant.components import tellduslive
|
from homeassistant.components import tellduslive
|
||||||
|
|
||||||
@ -64,7 +66,8 @@ class TelldusLiveSensor(Entity):
|
|||||||
self._sensor_id = sensor_id
|
self._sensor_id = sensor_id
|
||||||
self._sensor_type = sensor_type
|
self._sensor_type = sensor_type
|
||||||
self._state = None
|
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._last_update = None
|
||||||
self._battery_level = None
|
self._battery_level = None
|
||||||
self.update()
|
self.update()
|
||||||
|
@ -9,16 +9,20 @@ https://home-assistant.io/components/sensor.template/
|
|||||||
"""
|
"""
|
||||||
import logging
|
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.core import EVENT_STATE_CHANGED
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_FRIENDLY_NAME,
|
ATTR_FRIENDLY_NAME,
|
||||||
CONF_VALUE_TEMPLATE,
|
CONF_VALUE_TEMPLATE,
|
||||||
ATTR_UNIT_OF_MEASUREMENT)
|
ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
|
||||||
from homeassistant.util import template
|
from homeassistant.util import template, slugify
|
||||||
from homeassistant.exceptions import TemplateError
|
from homeassistant.exceptions import TemplateError
|
||||||
|
|
||||||
|
from homeassistant.components.sensor import DOMAIN
|
||||||
|
|
||||||
|
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
CONF_SENSORS = 'sensors'
|
CONF_SENSORS = 'sensors'
|
||||||
STATE_ERROR = 'error'
|
STATE_ERROR = 'error'
|
||||||
@ -34,9 +38,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
for device, device_config in config[CONF_SENSORS].items():
|
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):
|
if not isinstance(device_config, dict):
|
||||||
_LOGGER.error("Missing configuration data for sensor %s", device)
|
_LOGGER.error("Missing configuration data for sensor %s", device)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
|
friendly_name = device_config.get(ATTR_FRIENDLY_NAME, device)
|
||||||
unit_of_measurement = device_config.get(ATTR_UNIT_OF_MEASUREMENT)
|
unit_of_measurement = device_config.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
state_template = device_config.get(CONF_VALUE_TEMPLATE)
|
state_template = device_config.get(CONF_VALUE_TEMPLATE)
|
||||||
@ -44,14 +55,16 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Missing %s for sensor %s", CONF_VALUE_TEMPLATE, device)
|
"Missing %s for sensor %s", CONF_VALUE_TEMPLATE, device)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
sensors.append(
|
sensors.append(
|
||||||
SensorTemplate(
|
SensorTemplate(
|
||||||
hass,
|
hass,
|
||||||
|
device,
|
||||||
friendly_name,
|
friendly_name,
|
||||||
unit_of_measurement,
|
unit_of_measurement,
|
||||||
state_template)
|
state_template)
|
||||||
)
|
)
|
||||||
if sensors is None:
|
if not sensors:
|
||||||
_LOGGER.error("No sensors added")
|
_LOGGER.error("No sensors added")
|
||||||
return False
|
return False
|
||||||
add_devices(sensors)
|
add_devices(sensors)
|
||||||
@ -64,10 +77,15 @@ class SensorTemplate(Entity):
|
|||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
hass,
|
hass,
|
||||||
|
device_id,
|
||||||
friendly_name,
|
friendly_name,
|
||||||
unit_of_measurement,
|
unit_of_measurement,
|
||||||
state_template):
|
state_template):
|
||||||
|
|
||||||
|
self.entity_id = generate_entity_id(
|
||||||
|
ENTITY_ID_FORMAT, device_id,
|
||||||
|
hass=hass)
|
||||||
|
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self._name = friendly_name
|
self._name = friendly_name
|
||||||
self._unit_of_measurement = unit_of_measurement
|
self._unit_of_measurement = unit_of_measurement
|
||||||
|
@ -28,7 +28,7 @@ SENSOR_TYPES = {
|
|||||||
'temperature': ['Temperature', '°C'],
|
'temperature': ['Temperature', '°C'],
|
||||||
'windSpeed': ['Wind speed', 'm/s'],
|
'windSpeed': ['Wind speed', 'm/s'],
|
||||||
'windGust': ['Wind gust', 'm/s'],
|
'windGust': ['Wind gust', 'm/s'],
|
||||||
'pressure': ['Pressure', 'mbar'],
|
'pressure': ['Pressure', 'hPa'],
|
||||||
'windDirection': ['Wind direction', '°'],
|
'windDirection': ['Wind direction', '°'],
|
||||||
'humidity': ['Humidity', '%'],
|
'humidity': ['Humidity', '%'],
|
||||||
'fog': ['Fog', '%'],
|
'fog': ['Fog', '%'],
|
||||||
|
@ -126,6 +126,7 @@ ATTR_GPS_ACCURACY = 'gps_accuracy'
|
|||||||
|
|
||||||
# #### SERVICES ####
|
# #### SERVICES ####
|
||||||
SERVICE_HOMEASSISTANT_STOP = "stop"
|
SERVICE_HOMEASSISTANT_STOP = "stop"
|
||||||
|
SERVICE_HOMEASSISTANT_RESTART = "restart"
|
||||||
|
|
||||||
SERVICE_TURN_ON = 'turn_on'
|
SERVICE_TURN_ON = 'turn_on'
|
||||||
SERVICE_TURN_OFF = 'turn_off'
|
SERVICE_TURN_OFF = 'turn_off'
|
||||||
|
@ -16,7 +16,8 @@ from collections import namedtuple
|
|||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
__version__, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
|
__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_CALL_SERVICE, ATTR_NOW, ATTR_DOMAIN, ATTR_SERVICE, MATCH_ALL,
|
||||||
EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED,
|
EVENT_SERVICE_EXECUTED, ATTR_SERVICE_CALL_ID, EVENT_SERVICE_REGISTERED,
|
||||||
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_FRIENDLY_NAME, ATTR_SERVICE_DATA)
|
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_FRIENDLY_NAME, ATTR_SERVICE_DATA)
|
||||||
@ -47,6 +48,9 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
# Temporary to support deprecated methods
|
# Temporary to support deprecated methods
|
||||||
_MockHA = namedtuple("MockHomeAssistant", ['bus'])
|
_MockHA = namedtuple("MockHomeAssistant", ['bus'])
|
||||||
|
|
||||||
|
# The exit code to send to request a restart
|
||||||
|
RESTART_EXIT_CODE = 100
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistant(object):
|
class HomeAssistant(object):
|
||||||
"""Root object of the Home Assistant home automation."""
|
"""Root object of the Home Assistant home automation."""
|
||||||
@ -70,28 +74,33 @@ class HomeAssistant(object):
|
|||||||
def block_till_stopped(self):
|
def block_till_stopped(self):
|
||||||
"""Register service homeassistant/stop and will block until called."""
|
"""Register service homeassistant/stop and will block until called."""
|
||||||
request_shutdown = threading.Event()
|
request_shutdown = threading.Event()
|
||||||
|
request_restart = threading.Event()
|
||||||
|
|
||||||
def stop_homeassistant(*args):
|
def stop_homeassistant(*args):
|
||||||
"""Stop Home Assistant."""
|
"""Stop Home Assistant."""
|
||||||
request_shutdown.set()
|
request_shutdown.set()
|
||||||
|
|
||||||
|
def restart_homeassistant(*args):
|
||||||
|
"""Reset Home Assistant."""
|
||||||
|
request_restart.set()
|
||||||
|
request_shutdown.set()
|
||||||
|
|
||||||
self.services.register(
|
self.services.register(
|
||||||
DOMAIN, SERVICE_HOMEASSISTANT_STOP, stop_homeassistant)
|
DOMAIN, SERVICE_HOMEASSISTANT_STOP, stop_homeassistant)
|
||||||
|
self.services.register(
|
||||||
|
DOMAIN, SERVICE_HOMEASSISTANT_RESTART, restart_homeassistant)
|
||||||
|
|
||||||
if os.name != "nt":
|
try:
|
||||||
try:
|
signal.signal(signal.SIGTERM, stop_homeassistant)
|
||||||
signal.signal(signal.SIGTERM, stop_homeassistant)
|
except ValueError:
|
||||||
except ValueError:
|
_LOGGER.warning(
|
||||||
_LOGGER.warning(
|
'Could not bind to SIGTERM. Are you running in a thread?')
|
||||||
'Could not bind to SIGQUIT. Are you running in a thread?')
|
|
||||||
|
|
||||||
while not request_shutdown.isSet():
|
while not request_shutdown.isSet():
|
||||||
try:
|
time.sleep(1)
|
||||||
time.sleep(1)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
break
|
|
||||||
|
|
||||||
self.stop()
|
self.stop()
|
||||||
|
return RESTART_EXIT_CODE if request_restart.isSet() else 0
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""Stop Home Assistant and shuts down all threads."""
|
"""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:
|
with open(fname, encoding='utf-8') as conf_file:
|
||||||
# If configuration file is empty YAML returns None
|
# If configuration file is empty YAML returns None
|
||||||
# We convert that to an empty dict
|
# We convert that to an empty dict
|
||||||
return yaml.load(conf_file) or {}
|
return yaml.safe_load(conf_file) or {}
|
||||||
except yaml.YAMLError:
|
except yaml.YAMLError:
|
||||||
error = 'Error reading YAML configuration file {}'.format(fname)
|
error = 'Error reading YAML configuration file {}'.format(fname)
|
||||||
_LOGGER.exception(error)
|
_LOGGER.exception(error)
|
||||||
@ -45,6 +45,6 @@ def _ordered_dict(loader, node):
|
|||||||
return OrderedDict(loader.construct_pairs(node))
|
return OrderedDict(loader.construct_pairs(node))
|
||||||
|
|
||||||
|
|
||||||
yaml.add_constructor('!include', _include_yaml)
|
yaml.SafeLoader.add_constructor('!include', _include_yaml)
|
||||||
yaml.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
|
yaml.SafeLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
|
||||||
_ordered_dict)
|
_ordered_dict)
|
||||||
|
@ -100,7 +100,7 @@ insteon_hub==0.4.5
|
|||||||
jsonrpc-requests==0.1
|
jsonrpc-requests==0.1
|
||||||
|
|
||||||
# homeassistant.components.light.lifx
|
# homeassistant.components.light.lifx
|
||||||
liffylights==0.9.3
|
liffylights==0.9.4
|
||||||
|
|
||||||
# homeassistant.components.light.limitlessled
|
# homeassistant.components.light.limitlessled
|
||||||
limitlessled==1.0.0
|
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.
|
Tests template sensor.
|
||||||
"""
|
"""
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
import homeassistant.core as ha
|
import homeassistant.core as ha
|
||||||
import homeassistant.components.sensor as sensor
|
import homeassistant.components.sensor as sensor
|
||||||
@ -56,8 +53,54 @@ class TestTemplateSensor:
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
self.hass.states.set('sensor.test_state', 'Works')
|
self.hass.states.set('sensor.test_state', 'Works')
|
||||||
self.hass.pool.block_till_done()
|
self.hass.pool.block_till_done()
|
||||||
state = self.hass.states.get('sensor.test_template_sensor')
|
state = self.hass.states.get('sensor.test_template_sensor')
|
||||||
assert state.state == 'error'
|
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')
|
state = self.hass.states.get('sensor.yr_symbol')
|
||||||
|
|
||||||
|
assert '46' == state.state
|
||||||
assert state.state.isnumeric()
|
assert state.state.isnumeric()
|
||||||
assert state.attributes.get('unit_of_measurement') is None
|
assert state.attributes.get('unit_of_measurement') is None
|
||||||
|
|
||||||
def test_custom_setup(self, betamax_session):
|
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',
|
with patch('homeassistant.components.sensor.yr.requests.Session',
|
||||||
return_value=betamax_session):
|
return_value=betamax_session):
|
||||||
assert sensor.setup(self.hass, {
|
with patch('homeassistant.components.sensor.yr.dt_util.utcnow',
|
||||||
'sensor': {
|
return_value=now):
|
||||||
'platform': 'yr',
|
assert sensor.setup(self.hass, {
|
||||||
'elevation': 0,
|
'sensor': {
|
||||||
'monitored_conditions': {
|
'platform': 'yr',
|
||||||
'pressure',
|
'elevation': 0,
|
||||||
'windDirection',
|
'monitored_conditions': {
|
||||||
'humidity',
|
'pressure',
|
||||||
'fog',
|
'windDirection',
|
||||||
'windSpeed'
|
'humidity',
|
||||||
|
'fog',
|
||||||
|
'windSpeed'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
|
|
||||||
state = self.hass.states.get('sensor.yr_pressure')
|
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')
|
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')
|
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')
|
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')
|
state = self.hass.states.get('sensor.yr_wind_speed')
|
||||||
assert 'm/s', state.attributes.get('unit_of_measurement')
|
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):
|
with self.assertRaises(HomeAssistantError):
|
||||||
config_util.load_yaml_config_file(YAML_PATH)
|
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):
|
def test_load_yaml_config_preserves_key_order(self):
|
||||||
with open(YAML_PATH, 'w') as f:
|
with open(YAML_PATH, 'w') as f:
|
||||||
f.write('hello: 0\n')
|
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=protected-access,too-many-public-methods
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
import os
|
import os
|
||||||
|
import signal
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
import time
|
import time
|
||||||
@ -79,15 +80,15 @@ class TestHomeAssistant(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertFalse(blocking_thread.is_alive())
|
self.assertFalse(blocking_thread.is_alive())
|
||||||
|
|
||||||
def test_stopping_with_keyboardinterrupt(self):
|
def test_stopping_with_sigterm(self):
|
||||||
calls = []
|
calls = []
|
||||||
self.hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
|
self.hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
|
||||||
lambda event: calls.append(1))
|
lambda event: calls.append(1))
|
||||||
|
|
||||||
def raise_keyboardinterrupt(length):
|
def send_sigterm(length):
|
||||||
raise KeyboardInterrupt
|
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.hass.block_till_stopped()
|
||||||
|
|
||||||
self.assertEqual(1, len(calls))
|
self.assertEqual(1, len(calls))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user