Move core stuff into Home Assistant integration ()

* Move core stuff into Home Assistant integration

* Lint
pull/22419/head
Paulus Schoutsen 2019-03-26 05:38:33 -07:00 committed by Pascal Vizeli
parent bad0a8b342
commit 65432ba552
15 changed files with 175 additions and 170 deletions
homeassistant
tests
components
conversation
emulated_hue
generic_thermostat
google_assistant

View File

@ -9,10 +9,10 @@ from typing import Any, Optional, Dict, Set
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
@ -139,7 +139,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")

View File

@ -7,33 +7,12 @@ Component design guidelines:
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.
@ -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)
@ -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

View File

@ -1,5 +1,5 @@
"""Provide configuration end points for Customize."""
from homeassistant.components import SERVICE_RELOAD_CORE_CONFIG
from homeassistant.components.homeassistant import SERVICE_RELOAD_CORE_CONFIG
from homeassistant.config import DATA_CUSTOMIZE
from homeassistant.core import DOMAIN
import homeassistant.helpers.config_validation as cv

View File

@ -6,7 +6,7 @@ import os
import voluptuous as vol
from homeassistant.auth.const import GROUP_ID_ADMIN
from homeassistant.components import SERVICE_CHECK_CONFIG
from homeassistant.components.homeassistant import SERVICE_CHECK_CONFIG
import homeassistant.config as conf_util
from homeassistant.const import (
ATTR_NAME, SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP)

View File

@ -0,0 +1,137 @@
"""Integration providing core pieces of infrastructure."""
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
_LOGGER = logging.getLogger(__name__)
DOMAIN = ha.DOMAIN
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
})
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

View File

@ -9,8 +9,8 @@ from homeassistant.const import (
from homeassistant.core import State
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.state import HASS_DOMAIN, async_reproduce_state
from homeassistant.components.scene import STATES, Scene
from . import STATES, Scene
PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): HASS_DOMAIN,

View File

@ -5,7 +5,6 @@ import pytest
from homeassistant.core import DOMAIN as HASS_DOMAIN
from homeassistant.setup import async_setup_component
from homeassistant.components import conversation
import homeassistant.components as component
from homeassistant.components.cover import (SERVICE_OPEN_COVER)
from homeassistant.helpers import intent
@ -16,7 +15,7 @@ async def test_calling_intent(hass):
"""Test calling an intent from a conversation."""
intents = async_mock_intent(hass, 'OrderBeer')
result = await component.async_setup(hass, {})
result = await async_setup_component(hass, 'homeassistant', {})
assert result
result = await async_setup_component(hass, 'conversation', {
@ -146,7 +145,7 @@ async def test_http_processing_intent(hass, hass_client):
@pytest.mark.parametrize('sentence', ('turn on kitchen', 'turn kitchen on'))
async def test_turn_on_intent(hass, sentence):
"""Test calling the turn on intent."""
result = await component.async_setup(hass, {})
result = await async_setup_component(hass, 'homeassistant', {})
assert result
result = await async_setup_component(hass, 'conversation', {})
@ -197,7 +196,7 @@ async def test_cover_intents_loading(hass):
@pytest.mark.parametrize('sentence', ('turn off kitchen', 'turn kitchen off'))
async def test_turn_off_intent(hass, sentence):
"""Test calling the turn on intent."""
result = await component.async_setup(hass, {})
result = await async_setup_component(hass, 'homeassistant', {})
assert result
result = await async_setup_component(hass, 'conversation', {})
@ -222,7 +221,7 @@ async def test_turn_off_intent(hass, sentence):
@pytest.mark.parametrize('sentence', ('toggle kitchen', 'kitchen toggle'))
async def test_toggle_intent(hass, sentence):
"""Test calling the turn on intent."""
result = await component.async_setup(hass, {})
result = await async_setup_component(hass, 'homeassistant', {})
assert result
result = await async_setup_component(hass, 'conversation', {})
@ -246,7 +245,7 @@ async def test_toggle_intent(hass, sentence):
async def test_http_api(hass, hass_client):
"""Test the HTTP conversation API."""
result = await component.async_setup(hass, {})
result = await async_setup_component(hass, 'homeassistant', {})
assert result
result = await async_setup_component(hass, 'conversation', {})
@ -270,7 +269,7 @@ async def test_http_api(hass, hass_client):
async def test_http_api_wrong_data(hass, hass_client):
"""Test the HTTP conversation API."""
result = await component.async_setup(hass, {})
result = await async_setup_component(hass, 'homeassistant', {})
assert result
result = await async_setup_component(hass, 'conversation', {})

View File

@ -2,7 +2,7 @@
from homeassistant.components.cover import (SERVICE_OPEN_COVER,
SERVICE_CLOSE_COVER)
from homeassistant.components import intent
from homeassistant.helpers import intent
import homeassistant.components as comps
from tests.common import async_mock_service

View File

@ -8,8 +8,7 @@ from aiohttp.hdrs import CONTENT_TYPE
import pytest
from tests.common import get_test_instance_port
from homeassistant import core, const, setup
import homeassistant.components as core_components
from homeassistant import const, setup
from homeassistant.components import (
fan, http, light, script, emulated_hue, media_player, cover, climate)
from homeassistant.components.emulated_hue import Config
@ -33,8 +32,8 @@ JSON_HEADERS = {CONTENT_TYPE: const.CONTENT_TYPE_JSON}
def hass_hue(loop, hass):
"""Set up a Home Assistant instance for these tests."""
# We need to do this to get access to homeassistant/turn_(on,off)
loop.run_until_complete(
core_components.async_setup(hass, {core.DOMAIN: {}}))
loop.run_until_complete(setup.async_setup_component(
hass, 'homeassistant', {}))
loop.run_until_complete(setup.async_setup_component(
hass, http.DOMAIN,

View File

@ -23,7 +23,6 @@ from homeassistant.util.unit_system import METRIC_SYSTEM
from homeassistant.components import input_boolean, switch
from homeassistant.components.climate.const import (
ATTR_OPERATION_MODE, STATE_HEAT, STATE_COOL, DOMAIN)
import homeassistant.components as comps
from tests.common import assert_setup_component, mock_restore_cache
from tests.components.climate import common
@ -68,7 +67,7 @@ def setup_comp_1(hass):
"""Initialize components."""
hass.config.units = METRIC_SYSTEM
assert hass.loop.run_until_complete(
comps.async_setup(hass, {})
async_setup_component(hass, 'homeassistant', {})
)

View File

@ -8,7 +8,7 @@ import pytest
from homeassistant import core, const, setup
from homeassistant.components import (
fan, cover, light, switch, lock, async_setup, media_player)
fan, cover, light, switch, lock, media_player)
from homeassistant.components.climate import const as climate
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES
from homeassistant.components import google_assistant as ga
@ -56,7 +56,7 @@ def assistant_client(loop, hass, aiohttp_client):
def hass_fixture(loop, hass):
"""Set up a Home Assistant instance for these tests."""
# We need to do this to get access to homeassistant/turn_(on,off)
loop.run_until_complete(async_setup(hass, {core.DOMAIN: {}}))
loop.run_until_complete(setup.async_setup_component(hass, core.DOMAIN, {}))
loop.run_until_complete(
setup.async_setup_component(hass, light.DOMAIN, {

View File

@ -0,0 +1 @@
"""Tests for the Home Assistant integration to provide core functionality."""

View File

@ -12,7 +12,8 @@ from homeassistant.const import (
SERVICE_HOMEASSISTANT_STOP, SERVICE_TURN_ON, SERVICE_TURN_OFF,
SERVICE_TOGGLE)
import homeassistant.components as comps
from homeassistant.components import (
from homeassistant.setup import async_setup_component
from homeassistant.components.homeassistant import (
SERVICE_CHECK_CONFIG, SERVICE_RELOAD_CORE_CONFIG)
import homeassistant.helpers.intent as intent
from homeassistant.exceptions import HomeAssistantError
@ -97,7 +98,8 @@ class TestComponentsCore(unittest.TestCase):
"""Set up things to be run when tests are started."""
self.hass = get_test_home_assistant()
assert run_coroutine_threadsafe(
comps.async_setup(self.hass, {}), self.hass.loop
async_setup_component(self.hass, 'homeassistant', {}),
self.hass.loop
).result()
self.hass.states.set('light.Bowl', STATE_ON)
@ -186,7 +188,7 @@ class TestComponentsCore(unittest.TestCase):
assert state.attributes.get('hello') == 'world'
@patch('homeassistant.config.os.path.isfile', Mock(return_value=True))
@patch('homeassistant.components._LOGGER.error')
@patch('homeassistant.components.homeassistant._LOGGER.error')
@patch('homeassistant.config.async_process_ha_core_config')
def test_reload_core_with_wrong_conf(self, mock_process, mock_error):
"""Test reload core conf service."""
@ -244,7 +246,7 @@ class TestComponentsCore(unittest.TestCase):
async def test_turn_on_intent(hass):
"""Test HassTurnOn intent."""
result = await comps.async_setup(hass, {})
result = await async_setup_component(hass, 'homeassistant', {})
assert result
hass.states.async_set('light.test_light', 'off')
@ -265,7 +267,7 @@ async def test_turn_on_intent(hass):
async def test_turn_off_intent(hass):
"""Test HassTurnOff intent."""
result = await comps.async_setup(hass, {})
result = await async_setup_component(hass, 'homeassistant', {})
assert result
hass.states.async_set('light.test_light', 'on')
@ -286,7 +288,7 @@ async def test_turn_off_intent(hass):
async def test_toggle_intent(hass):
"""Test HassToggle intent."""
result = await comps.async_setup(hass, {})
result = await async_setup_component(hass, 'homeassistant', {})
assert result
hass.states.async_set('light.test_light', 'off')
@ -310,7 +312,7 @@ async def test_turn_on_multiple_intent(hass):
This tests that matching finds the proper entity among similar names.
"""
result = await comps.async_setup(hass, {})
result = await async_setup_component(hass, 'homeassistant', {})
assert result
hass.states.async_set('light.test_light', 'off')
@ -333,7 +335,7 @@ async def test_turn_on_multiple_intent(hass):
async def test_turn_on_to_not_block_for_domains_without_service(hass):
"""Test if turn_on is blocking domain with no service."""
await comps.async_setup(hass, {})
await async_setup_component(hass, 'homeassistant', {})
async_mock_service(hass, 'light', SERVICE_TURN_ON)
hass.states.async_set('light.Bowl', STATE_ON)
hass.states.async_set('light.Ceiling', STATE_OFF)
@ -359,7 +361,7 @@ async def test_turn_on_to_not_block_for_domains_without_service(hass):
async def test_entity_update(hass):
"""Test being able to call entity update."""
await comps.async_setup(hass, {})
await async_setup_component(hass, 'homeassistant', {})
with patch('homeassistant.helpers.entity_component.async_update_entity',
return_value=mock_coro()) as mock_update:

View File

@ -1 +0,0 @@
"""Tests for the init component."""

View File

@ -5,7 +5,7 @@ import unittest
from unittest.mock import patch
import homeassistant.core as ha
import homeassistant.components as core_components
from homeassistant.setup import async_setup_component
from homeassistant.const import (SERVICE_TURN_ON, SERVICE_TURN_OFF)
from homeassistant.util.async_ import run_coroutine_threadsafe
from homeassistant.util import dt as dt_util
@ -88,8 +88,8 @@ class TestStateHelpers(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name
"""Run when tests are started."""
self.hass = get_test_home_assistant()
run_coroutine_threadsafe(core_components.async_setup(
self.hass, {}), self.hass.loop).result()
run_coroutine_threadsafe(async_setup_component(
self.hass, 'homeassistant', {}), self.hass.loop).result()
def tearDown(self): # pylint: disable=invalid-name
"""Stop when tests are finished."""