Skip to content

Commit

Permalink
Merge branch 'dev' into current
Browse files Browse the repository at this point in the history
* dev: (69 commits)
  Use voluptuous error string for websocket validation error (home-assistant#21883)
  Axis devices support device registry (home-assistant#22367)
  Bootstrap to start registry loading early (home-assistant#22321)
  Add support for yeelight ceiling ambilight (home-assistant#22346)
  Update ZHA component CODEOWNERS (home-assistant#22452)
  Add myself as codeowner for yeelight component (home-assistant#22438)
  Update ha-ffmpeg 2.0 (home-assistant#22427)
  Lower severity level of log messages from http.view (home-assistant#21091)
  Fix test name (home-assistant#22421)
  Adding conf for deep standby, wake and specific source bouquet of Enigma2 (home-assistant#22393)
  Update homeassistant-pyozw to 0.1.3 (home-assistant#22433)
  ciscomobilityexpress pypi version update (home-assistant#22431)
  Use dispatcher for netgear_lte state updates (home-assistant#22328)
  Update translate, fix dev build error (home-assistant#22419)
  Add missing append (home-assistant#22414)
  Enable hass.io panel without ping (home-assistant#22388)
  Migrate tts (home-assistant#22403)
  Changed busy error to warning (home-assistant#22398)
  Fix yeelight state update (home-assistant#22373)
  zha fixes (home-assistant#22381)
  ...
  • Loading branch information
worm-ee committed Mar 27, 2019
2 parents a36df96 + 29ad396 commit 3a45c31
Show file tree
Hide file tree
Showing 191 changed files with 5,079 additions and 1,450 deletions.
2 changes: 1 addition & 1 deletion .coveragerc
Expand Up @@ -36,7 +36,6 @@ omit =
homeassistant/components/arlo/*
homeassistant/components/asterisk_mbox/*
homeassistant/components/august/*
homeassistant/components/axis/*
homeassistant/components/bbb_gpio/*
homeassistant/components/arest/binary_sensor.py
homeassistant/components/concord232/binary_sensor.py
Expand Down Expand Up @@ -83,6 +82,7 @@ omit =
homeassistant/components/proliphix/climate.py
homeassistant/components/radiotherm/climate.py
homeassistant/components/sensibo/climate.py
homeassistant/components/tfiac/climate.py
homeassistant/components/touchline/climate.py
homeassistant/components/venstar/climate.py
homeassistant/components/zhong_hong/climate.py
Expand Down
6 changes: 4 additions & 2 deletions CODEOWNERS
Validating CODEOWNERS rules …
Expand Up @@ -241,15 +241,16 @@ homeassistant/components/tautulli/sensor.py @ludeeus
homeassistant/components/tellduslive/* @fredrike
homeassistant/components/template/cover.py @PhracturedBlue
homeassistant/components/tesla/* @zabuldon
homeassistant/components/tfiac/* @fredrike @mellado
homeassistant/components/thethingsnetwork/* @fabaff
homeassistant/components/threshold/binary_sensor.py @fabaff
homeassistant/components/tibber/* @danielhiversen
homeassistant/components/tile/device_tracker.py @bachya
homeassistant/components/time_date/sensor.py @fabaff
homeassistant/components/toon/* @frenck
homeassistant/components/tplink/* @rytilahti
homeassistant/components/traccar/device_tracker.py @ludeeus
homeassistant/components/tradfri/* @ggravlingen
homeassistant/components/toon/* @frenck

# U
homeassistant/components/uber/sensor.py @robbiet480
Expand Down Expand Up @@ -277,12 +278,13 @@ homeassistant/components/xiaomi_tv/media_player.py @fattdev

# Y
homeassistant/components/yamaha_musiccast/* @jalmeroth
homeassistant/components/yeelight/light.py @rytilahti
homeassistant/components/yeelight/* @rytilahti @zewelor
homeassistant/components/yeelightsunflower/light.py @lindsaymarkward
homeassistant/components/yi/camera.py @bachya

# Z
homeassistant/components/zeroconf/* @robbiet480
homeassistant/components/zha/* @dmulcahey @adminiuga
homeassistant/components/zoneminder/* @rohankapoorcom

# Other code
Expand Down
17 changes: 12 additions & 5 deletions homeassistant/bootstrap.py
@@ -1,4 +1,5 @@
"""Provide methods to bootstrap a Home Assistant instance."""
import asyncio
import logging
import logging.handlers
import os
Expand All @@ -9,10 +10,10 @@

import voluptuous as vol

from homeassistant import (
core, config as conf_util, config_entries, components as core_components,
loader)
from homeassistant.components import persistent_notification
from homeassistant import core, config as conf_util, config_entries, loader
from homeassistant.components import (
persistent_notification, homeassistant as core_component
)
from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE
from homeassistant.setup import async_setup_component
from homeassistant.util.logging import AsyncHandler
Expand Down Expand Up @@ -139,7 +140,7 @@ async def async_from_config_dict(config: Dict[str, Any],
pass

# setup components
res = await core_components.async_setup(hass, config)
res = await core_component.async_setup(hass, config)
if not res:
_LOGGER.error("Home Assistant core failed to initialize. "
"Further initialization aborted")
Expand All @@ -157,6 +158,12 @@ async def async_from_config_dict(config: Dict[str, Any],

await hass.async_block_till_done()

# Kick off loading the registries. They don't need to be awaited.
asyncio.gather(
hass.helpers.device_registry.async_get_registry(),
hass.helpers.entity_registry.async_get_registry(),
hass.helpers.area_registry.async_get_registry())

# stage 1
for component in components:
if component in FIRST_INIT_COMPONENT:
Expand Down
135 changes: 2 additions & 133 deletions homeassistant/components/__init__.py
Expand Up @@ -7,33 +7,12 @@
format "<DOMAIN>.<OBJECT_ID>".
- Each component should publish services only under its own domain.
"""
import asyncio
import itertools as it
import logging
from typing import Awaitable

import voluptuous as vol

import homeassistant.core as ha
import homeassistant.config as conf_util
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.service import async_extract_entity_ids
from homeassistant.helpers import intent
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_TOGGLE,
SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART,
RESTART_EXIT_CODE)
from homeassistant.helpers import config_validation as cv
from homeassistant.core import split_entity_id

_LOGGER = logging.getLogger(__name__)

SERVICE_RELOAD_CORE_CONFIG = 'reload_core_config'
SERVICE_CHECK_CONFIG = 'check_config'
SERVICE_UPDATE_ENTITY = 'update_entity'
SCHEMA_UPDATE_ENTITY = vol.Schema({
ATTR_ENTITY_ID: cv.entity_ids
})


def is_on(hass, entity_id=None):
"""Load up the module to call the is_on method.
Expand All @@ -46,7 +25,7 @@ def is_on(hass, entity_id=None):
entity_ids = hass.states.entity_ids()

for ent_id in entity_ids:
domain = ha.split_entity_id(ent_id)[0]
domain = split_entity_id(ent_id)[0]

try:
component = getattr(hass.components, domain)
Expand All @@ -64,113 +43,3 @@ def is_on(hass, entity_id=None):
return True

return False


async def async_setup(hass: ha.HomeAssistant, config: dict) -> Awaitable[bool]:
"""Set up general services related to Home Assistant."""
async def async_handle_turn_service(service):
"""Handle calls to homeassistant.turn_on/off."""
entity_ids = await async_extract_entity_ids(hass, service)

# Generic turn on/off method requires entity id
if not entity_ids:
_LOGGER.error(
"homeassistant/%s cannot be called without entity_id",
service.service)
return

# Group entity_ids by domain. groupby requires sorted data.
by_domain = it.groupby(sorted(entity_ids),
lambda item: ha.split_entity_id(item)[0])

tasks = []

for domain, ent_ids in by_domain:
# We want to block for all calls and only return when all calls
# have been processed. If a service does not exist it causes a 10
# second delay while we're blocking waiting for a response.
# But services can be registered on other HA instances that are
# listening to the bus too. So as an in between solution, we'll
# block only if the service is defined in the current HA instance.
blocking = hass.services.has_service(domain, service.service)

# Create a new dict for this call
data = dict(service.data)

# ent_ids is a generator, convert it to a list.
data[ATTR_ENTITY_ID] = list(ent_ids)

tasks.append(hass.services.async_call(
domain, service.service, data, blocking))

await asyncio.wait(tasks, loop=hass.loop)

hass.services.async_register(
ha.DOMAIN, SERVICE_TURN_OFF, async_handle_turn_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_TURN_ON, async_handle_turn_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_TOGGLE, async_handle_turn_service)
hass.helpers.intent.async_register(intent.ServiceIntentHandler(
intent.INTENT_TURN_ON, ha.DOMAIN, SERVICE_TURN_ON, "Turned {} on"))
hass.helpers.intent.async_register(intent.ServiceIntentHandler(
intent.INTENT_TURN_OFF, ha.DOMAIN, SERVICE_TURN_OFF,
"Turned {} off"))
hass.helpers.intent.async_register(intent.ServiceIntentHandler(
intent.INTENT_TOGGLE, ha.DOMAIN, SERVICE_TOGGLE, "Toggled {}"))

async def async_handle_core_service(call):
"""Service handler for handling core services."""
if call.service == SERVICE_HOMEASSISTANT_STOP:
hass.async_create_task(hass.async_stop())
return

try:
errors = await conf_util.async_check_ha_config_file(hass)
except HomeAssistantError:
return

if errors:
_LOGGER.error(errors)
hass.components.persistent_notification.async_create(
"Config error. See dev-info panel for details.",
"Config validating", "{0}.check_config".format(ha.DOMAIN))
return

if call.service == SERVICE_HOMEASSISTANT_RESTART:
hass.async_create_task(hass.async_stop(RESTART_EXIT_CODE))

async def async_handle_update_service(call):
"""Service handler for updating an entity."""
tasks = [hass.helpers.entity_component.async_update_entity(entity)
for entity in call.data[ATTR_ENTITY_ID]]

if tasks:
await asyncio.wait(tasks)

hass.services.async_register(
ha.DOMAIN, SERVICE_HOMEASSISTANT_STOP, async_handle_core_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_HOMEASSISTANT_RESTART, async_handle_core_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_CHECK_CONFIG, async_handle_core_service)
hass.services.async_register(
ha.DOMAIN, SERVICE_UPDATE_ENTITY, async_handle_update_service,
schema=SCHEMA_UPDATE_ENTITY)

async def async_handle_reload_config(call):
"""Service handler for reloading core config."""
try:
conf = await conf_util.async_hass_config_yaml(hass)
except HomeAssistantError as err:
_LOGGER.error(err)
return

# auth only processed during startup
await conf_util.async_process_ha_core_config(
hass, conf.get(ha.DOMAIN) or {})

hass.services.async_register(
ha.DOMAIN, SERVICE_RELOAD_CORE_CONFIG, async_handle_reload_config)

return True
5 changes: 3 additions & 2 deletions homeassistant/components/amcrest/camera.py
Expand Up @@ -78,16 +78,17 @@ async def handle_async_mjpeg_stream(self, request):
self.hass, request, stream_coro)

# streaming via ffmpeg
from haffmpeg import CameraMjpeg
from haffmpeg.camera import CameraMjpeg

streaming_url = self._camera.rtsp_url(typeno=self._resolution)
stream = CameraMjpeg(self._ffmpeg.binary, loop=self.hass.loop)
await stream.open_camera(
streaming_url, extra_cmd=self._ffmpeg_arguments)

try:
stream_reader = await stream.get_reader()
return await async_aiohttp_proxy_stream(
self.hass, request, stream,
self.hass, request, stream_reader,
self._ffmpeg.ffmpeg_stream_content_type)
finally:
await stream.close()
Expand Down
42 changes: 32 additions & 10 deletions homeassistant/components/androidtv/media_player.py
Expand Up @@ -40,6 +40,8 @@
CONF_ADB_SERVER_PORT = 'adb_server_port'
CONF_APPS = 'apps'
CONF_GET_SOURCES = 'get_sources'
CONF_TURN_ON_COMMAND = 'turn_on_command'
CONF_TURN_OFF_COMMAND = 'turn_off_command'

DEFAULT_NAME = 'Android TV'
DEFAULT_PORT = 5555
Expand Down Expand Up @@ -79,7 +81,9 @@ def has_adb_files(value):
cv.port,
vol.Optional(CONF_GET_SOURCES, default=DEFAULT_GET_SOURCES): cv.boolean,
vol.Optional(CONF_APPS, default=dict()):
vol.Schema({cv.string: cv.string})
vol.Schema({cv.string: cv.string}),
vol.Optional(CONF_TURN_ON_COMMAND): cv.string,
vol.Optional(CONF_TURN_OFF_COMMAND): cv.string
})

# Translate from `AndroidTV` / `FireTV` reported state to HA state.
Expand Down Expand Up @@ -136,12 +140,16 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
else:
if aftv.DEVICE_CLASS == DEVICE_ANDROIDTV:
device = AndroidTVDevice(aftv, config[CONF_NAME],
config[CONF_APPS])
config[CONF_APPS],
config.get(CONF_TURN_ON_COMMAND),
config.get(CONF_TURN_OFF_COMMAND))
device_name = config[CONF_NAME] if CONF_NAME in config \
else 'Android TV'
else:
device = FireTVDevice(aftv, config[CONF_NAME], config[CONF_APPS],
config[CONF_GET_SOURCES])
config[CONF_GET_SOURCES],
config.get(CONF_TURN_ON_COMMAND),
config.get(CONF_TURN_OFF_COMMAND))
device_name = config[CONF_NAME] if CONF_NAME in config \
else 'Fire TV'

Expand Down Expand Up @@ -199,7 +207,8 @@ def _adb_exception_catcher(self, *args, **kwargs):
class ADBDevice(MediaPlayerDevice):
"""Representation of an Android TV or Fire TV device."""

def __init__(self, aftv, name, apps):
def __init__(self, aftv, name, apps, turn_on_command,
turn_off_command):
"""Initialize the Android TV / Fire TV device."""
from androidtv.constants import APPS, KEYS

Expand All @@ -209,6 +218,9 @@ def __init__(self, aftv, name, apps):
self._apps.update(apps)
self._keys = KEYS

self.turn_on_command = turn_on_command
self.turn_off_command = turn_off_command

# ADB exceptions to catch
if not self.aftv.adb_server_ip:
# Using "python-adb" (Python ADB implementation)
Expand Down Expand Up @@ -278,12 +290,18 @@ def media_play_pause(self):
@adb_decorator()
def turn_on(self):
"""Turn on the device."""
self.aftv.turn_on()
if self.turn_on_command:
self.aftv.adb_shell(self.turn_on_command)
else:
self.aftv.turn_on()

@adb_decorator()
def turn_off(self):
"""Turn off the device."""
self.aftv.turn_off()
if self.turn_off_command:
self.aftv.adb_shell(self.turn_off_command)
else:
self.aftv.turn_off()

@adb_decorator()
def media_previous_track(self):
Expand Down Expand Up @@ -311,9 +329,11 @@ def adb_command(self, cmd):
class AndroidTVDevice(ADBDevice):
"""Representation of an Android TV device."""

def __init__(self, aftv, name, apps):
def __init__(self, aftv, name, apps, turn_on_command,
turn_off_command):
"""Initialize the Android TV device."""
super().__init__(aftv, name, apps)
super().__init__(aftv, name, apps, turn_on_command,
turn_off_command)

self._device = None
self._muted = None
Expand Down Expand Up @@ -392,9 +412,11 @@ def volume_up(self):
class FireTVDevice(ADBDevice):
"""Representation of a Fire TV device."""

def __init__(self, aftv, name, apps, get_sources):
def __init__(self, aftv, name, apps, get_sources,
turn_on_command, turn_off_command):
"""Initialize the Fire TV device."""
super().__init__(aftv, name, apps)
super().__init__(aftv, name, apps, turn_on_command,
turn_off_command)

self._get_sources = get_sources
self._running_apps = None
Expand Down
5 changes: 3 additions & 2 deletions homeassistant/components/arlo/camera.py
Expand Up @@ -83,7 +83,7 @@ def _update_callback(self):

async def handle_async_mjpeg_stream(self, request):
"""Generate an HTTP MJPEG stream from the camera."""
from haffmpeg import CameraMjpeg
from haffmpeg.camera import CameraMjpeg
video = self._camera.last_video
if not video:
error_msg = \
Expand All @@ -97,8 +97,9 @@ async def handle_async_mjpeg_stream(self, request):
video.video_url, extra_cmd=self._ffmpeg_arguments)

try:
stream_reader = await stream.get_reader()
return await async_aiohttp_proxy_stream(
self.hass, request, stream,
self.hass, request, stream_reader,
self._ffmpeg.ffmpeg_stream_content_type)
finally:
await stream.close()
Expand Down

0 comments on commit 3a45c31

Please sign in to comment.