core/homeassistant/components/__init__.py

177 lines
6.4 KiB
Python
Raw Normal View History

"""
This package contains components that can be plugged into Home Assistant.
2014-01-05 02:24:30 +00:00
Component design guidelines:
2016-03-08 16:55:57 +00:00
- Each component defines a constant DOMAIN that is equal to its filename.
- Each component that tracks states should create state entity names in the
format "<DOMAIN>.<OBJECT_ID>".
- Each component should publish services only under its own domain.
"""
import asyncio
2014-04-13 19:59:45 +00:00
import itertools as it
import logging
from typing import Awaitable
2018-10-09 14:54:38 +00:00
import voluptuous as vol
2015-08-17 03:44:46 +00:00
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)
2018-10-09 14:54:38 +00:00
from homeassistant.helpers import config_validation as cv
2014-11-08 21:57:08 +00:00
_LOGGER = logging.getLogger(__name__)
SERVICE_RELOAD_CORE_CONFIG = 'reload_core_config'
SERVICE_CHECK_CONFIG = 'check_config'
2018-10-09 14:54:38 +00:00
SERVICE_UPDATE_ENTITY = 'update_entity'
SCHEMA_UPDATE_ENTITY = vol.Schema({
ATTR_ENTITY_ID: cv.entity_ids
2018-10-09 14:54:38 +00:00
})
def is_on(hass, entity_id=None):
2016-03-08 16:55:57 +00:00
"""Load up the module to call the is_on method.
If there is no entity id given we will check all.
"""
2014-04-13 19:59:45 +00:00
if entity_id:
entity_ids = hass.components.group.expand_entity_ids([entity_id])
2014-04-13 19:59:45 +00:00
else:
2014-11-29 07:19:59 +00:00
entity_ids = hass.states.entity_ids()
for ent_id in entity_ids:
domain = ha.split_entity_id(ent_id)[0]
try:
component = getattr(hass.components, domain)
except ImportError:
_LOGGER.error('Failed to call %s.is_on: component not found',
domain)
continue
if not hasattr(component, 'is_on'):
_LOGGER.warning("Component %s has no is_on method.", domain)
continue
if component.is_on(ent_id):
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)
2014-04-13 19:59:45 +00:00
# Generic turn on/off method requires entity id
if not entity_ids:
2014-11-08 21:57:08 +00:00
_LOGGER.error(
"homeassistant/%s cannot be called without entity_id",
service.service)
2014-04-13 19:59:45 +00:00
return
2014-04-13 19:59:45 +00:00
# Group entity_ids by domain. groupby requires sorted data.
by_domain = it.groupby(sorted(entity_ids),
2016-08-09 03:21:40 +00:00
lambda item: ha.split_entity_id(item)[0])
2014-04-13 19:59:45 +00:00
tasks = []
2014-04-13 19:59:45 +00:00
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
2018-01-27 19:58:27 +00:00
# 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)
2014-04-13 19:59:45 +00:00
# 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))
2018-10-09 14:54:38 +00:00
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)
2018-10-09 14:54:38 +00:00
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)
2018-10-09 14:54:38 +00:00
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