1
mirror of https://github.com/home-assistant/core synced 2024-07-12 07:21:24 +02:00

Add the ability to reload light/cover groups from yaml (#39250)

* Add the ability to reload light/cover groups from yaml

Update previous usage to reduce code duplication.

* Fix conflict from rebase
This commit is contained in:
J. Nick Koston 2020-08-25 18:13:43 -05:00 committed by GitHub
parent e109b04efe
commit 810df38f0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 175 additions and 56 deletions

View File

@ -34,6 +34,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity, async_generate_entity_id
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import async_track_state_change_event
from homeassistant.helpers.reload import async_reload_integration_platforms
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.loader import bind_hass
@ -57,6 +58,8 @@ ATTR_ALL = "all"
SERVICE_SET = "set"
SERVICE_REMOVE = "remove"
PLATFORMS = ["light", "cover"]
_LOGGER = logging.getLogger(__name__)
@ -219,6 +222,8 @@ async def async_setup(hass, config):
await component.async_add_entities(auto)
await async_reload_integration_platforms(hass, DOMAIN, PLATFORMS)
hass.services.async_register(
DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=vol.Schema({})
)

View File

@ -31,9 +31,10 @@ from homeassistant.core import callback
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.script import Script
from . import async_setup_reload_service
from .const import DOMAIN, PLATFORMS
from .template_entity import TemplateEntity
_LOGGER = logging.getLogger(__name__)
@ -112,7 +113,7 @@ async def _async_create_entities(hass, config):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Template Alarm Control Panels."""
await async_setup_reload_service(hass)
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities(await _async_create_entities(hass, config))

View File

@ -24,10 +24,10 @@ from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.template import result_as_boolean
from . import async_setup_reload_service
from .const import CONF_AVAILABILITY_TEMPLATE
from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS
from .template_entity import TemplateEntity
_LOGGER = logging.getLogger(__name__)
@ -97,7 +97,7 @@ async def _async_create_entities(hass, config):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the template binary sensors."""
await async_setup_reload_service(hass)
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities(await _async_create_entities(hass, config))

View File

@ -35,10 +35,10 @@ from homeassistant.core import callback
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.script import Script
from . import async_setup_reload_service
from .const import CONF_AVAILABILITY_TEMPLATE
from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS
from .template_entity import TemplateEntity
_LOGGER = logging.getLogger(__name__)
@ -152,7 +152,7 @@ async def _async_create_entities(hass, config):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Template cover."""
await async_setup_reload_service(hass)
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities(await _async_create_entities(hass, config))

View File

@ -32,10 +32,10 @@ from homeassistant.core import callback
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.script import Script
from . import async_setup_reload_service
from .const import CONF_AVAILABILITY_TEMPLATE
from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS
from .template_entity import TemplateEntity
_LOGGER = logging.getLogger(__name__)
@ -129,7 +129,7 @@ async def _async_create_entities(hass, config):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the template fans."""
await async_setup_reload_service(hass)
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities(await _async_create_entities(hass, config))

View File

@ -31,10 +31,10 @@ from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.script import Script
from . import async_setup_reload_service
from .const import CONF_AVAILABILITY_TEMPLATE
from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS
from .template_entity import TemplateEntity
_LOGGER = logging.getLogger(__name__)
@ -135,7 +135,7 @@ async def _async_create_entities(hass, config):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the template lights."""
await async_setup_reload_service(hass)
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities(await _async_create_entities(hass, config))

View File

@ -15,10 +15,10 @@ from homeassistant.const import (
from homeassistant.core import callback
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.script import Script
from . import async_setup_reload_service
from .const import CONF_AVAILABILITY_TEMPLATE
from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS
from .template_entity import TemplateEntity
_LOGGER = logging.getLogger(__name__)
@ -65,7 +65,7 @@ async def _async_create_entities(hass, config):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the template lock."""
await async_setup_reload_service(hass)
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities(await _async_create_entities(hass, config))

View File

@ -25,9 +25,9 @@ from homeassistant.core import callback
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity, async_generate_entity_id
from homeassistant.helpers.reload import async_setup_reload_service
from . import async_setup_reload_service
from .const import CONF_AVAILABILITY_TEMPLATE
from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS
from .template_entity import TemplateEntity
CONF_ATTRIBUTE_TEMPLATES = "attribute_templates"
@ -97,7 +97,7 @@ async def _async_create_entities(hass, config):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the template sensors."""
await async_setup_reload_service(hass)
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities(await _async_create_entities(hass, config))

View File

@ -23,11 +23,11 @@ from homeassistant.core import callback
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.script import Script
from . import async_setup_reload_service
from .const import CONF_AVAILABILITY_TEMPLATE
from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS
from .template_entity import TemplateEntity
_LOGGER = logging.getLogger(__name__)
@ -90,7 +90,7 @@ async def _async_create_entities(hass, config):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the template switches."""
await async_setup_reload_service(hass)
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities(await _async_create_entities(hass, config))

View File

@ -5,7 +5,7 @@ import voluptuous as vol
from homeassistant.components.vacuum import (
ATTR_FAN_SPEED,
DOMAIN,
DOMAIN as VACUUM_DOMAIN,
SERVICE_CLEAN_SPOT,
SERVICE_LOCATE,
SERVICE_PAUSE,
@ -41,10 +41,10 @@ from homeassistant.core import callback
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.script import Script
from . import async_setup_reload_service
from .const import CONF_AVAILABILITY_TEMPLATE
from .const import CONF_AVAILABILITY_TEMPLATE, DOMAIN, PLATFORMS
from .template_entity import TemplateEntity
_LOGGER = logging.getLogger(__name__)
@ -55,7 +55,7 @@ CONF_FAN_SPEED_LIST = "fan_speeds"
CONF_FAN_SPEED_TEMPLATE = "fan_speed_template"
CONF_ATTRIBUTE_TEMPLATES = "attribute_templates"
ENTITY_ID_FORMAT = DOMAIN + ".{}"
ENTITY_ID_FORMAT = VACUUM_DOMAIN + ".{}"
_VALID_STATES = [
STATE_CLEANING,
STATE_DOCKED,
@ -145,7 +145,7 @@ async def _async_create_entities(hass, config):
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the template vacuums."""
await async_setup_reload_service(hass)
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
async_add_entities(await _async_create_entities(hass, config))

View File

@ -56,7 +56,6 @@ from homeassistant.const import (
SERVICE_MEDIA_PREVIOUS_TRACK,
SERVICE_MEDIA_SEEK,
SERVICE_MEDIA_STOP,
SERVICE_RELOAD,
SERVICE_SHUFFLE_SET,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
@ -72,7 +71,7 @@ from homeassistant.const import (
from homeassistant.core import EVENT_HOMEASSISTANT_START, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.reload import async_reload_integration_platforms
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.service import async_call_from_config
_LOGGER = logging.getLogger(__name__)
@ -104,30 +103,11 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
extra=vol.REMOVE_EXTRA,
)
EVENT_UNIVERSAL_RELOADED = "event_universal_reloaded"
async def async_setup_reload_service(hass):
"""Create the reload service for the universal domain."""
if hass.services.has_service("universal", SERVICE_RELOAD):
return
async def _reload_config(call):
"""Reload the template universal config."""
await async_reload_integration_platforms(hass, "universal", ["media_player"])
hass.bus.async_fire(EVENT_UNIVERSAL_RELOADED, context=call.context)
hass.helpers.service.async_register_admin_service(
"universal", SERVICE_RELOAD, _reload_config
)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the universal media players."""
await async_setup_reload_service(hass)
await async_setup_reload_service(hass, "universal", ["media_player"])
player = UniversalMediaPlayer(
hass,

View File

@ -1,10 +1,12 @@
"""Class to reload platforms."""
import asyncio
import logging
from typing import Optional
from typing import Iterable, Optional
from homeassistant import config as conf_util
from homeassistant.core import callback
from homeassistant.const import SERVICE_RELOAD
from homeassistant.core import Event, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_per_platform
from homeassistant.helpers.entity_platform import DATA_ENTITY_PLATFORM, EntityPlatform
@ -15,7 +17,7 @@ _LOGGER = logging.getLogger(__name__)
async def async_reload_integration_platforms(
hass: HomeAssistantType, integration_name: str, integration_platforms: str
hass: HomeAssistantType, integration_name: str, integration_platforms: Iterable
) -> None:
"""Reload an integration's platforms.
@ -68,3 +70,32 @@ def async_get_platform(
return platform
return None
async def async_setup_reload_service(
hass: HomeAssistantType, domain: str, platforms: Iterable
) -> None:
"""Create the reload service for the domain."""
if hass.services.has_service(domain, SERVICE_RELOAD):
return
async def _reload_config(call: Event) -> None:
"""Reload the platforms."""
await async_reload_integration_platforms(hass, domain, platforms)
hass.bus.async_fire(f"event_{domain}_reloaded", context=call.context)
hass.helpers.service.async_register_admin_service(
domain, SERVICE_RELOAD, _reload_config
)
def setup_reload_service(
hass: HomeAssistantType, domain: str, platforms: Iterable
) -> None:
"""Sync version of async_setup_reload_service."""
asyncio.run_coroutine_threadsafe(
async_setup_reload_service(hass, domain, platforms), hass.loop,
).result()

View File

@ -1,5 +1,10 @@
"""The tests for the Group Light platform."""
from homeassistant.components.group import DOMAIN
from os import path
from asynctest.mock import patch
from homeassistant import config as hass_config
from homeassistant.components.group import DOMAIN, SERVICE_RELOAD
import homeassistant.components.group.light as group
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
@ -632,3 +637,48 @@ async def test_invalid_service_calls(hass):
mock_call.assert_called_once_with(
LIGHT_DOMAIN, SERVICE_TURN_ON, data, blocking=True, context=None
)
async def test_reload(hass):
"""Test the ability to reload lights."""
await async_setup_component(
hass,
LIGHT_DOMAIN,
{
LIGHT_DOMAIN: [
{"platform": "demo"},
{
"platform": DOMAIN,
"entities": [
"light.bed_light",
"light.ceiling_lights",
"light.kitchen_lights",
],
},
]
},
)
await hass.async_block_till_done()
await hass.async_block_till_done()
await hass.async_start()
await hass.async_block_till_done()
assert hass.states.get("light.light_group").state == STATE_ON
yaml_path = path.join(
_get_fixtures_base_path(), "fixtures", "group/configuration.yaml",
)
with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path):
await hass.services.async_call(
DOMAIN, SERVICE_RELOAD, {}, blocking=True,
)
await hass.async_block_till_done()
assert hass.states.get("light.light_group") is None
assert hass.states.get("light.master_hall_lights_g") is not None
assert hass.states.get("light.outside_patio_lights_g") is not None
def _get_fixtures_base_path():
return path.dirname(path.dirname(path.dirname(__file__)))

View File

@ -14,6 +14,7 @@ import homeassistant.components.media_player as media_player
import homeassistant.components.switch as switch
import homeassistant.components.universal.media_player as universal
from homeassistant.const import (
SERVICE_RELOAD,
STATE_OFF,
STATE_ON,
STATE_PAUSED,
@ -818,7 +819,7 @@ async def test_master_state_with_template(hass):
async def test_reload(hass):
"""Test the state_template option."""
"""Test reloading the media player from yaml."""
hass.states.async_set("input_boolean.test", STATE_OFF)
hass.states.async_set("media_player.mock1", STATE_OFF)
@ -863,7 +864,7 @@ async def test_reload(hass):
)
with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path):
await hass.services.async_call(
"universal", universal.SERVICE_RELOAD, {}, blocking=True,
"universal", SERVICE_RELOAD, {}, blocking=True,
)
await hass.async_block_till_done()

11
tests/fixtures/group/configuration.yaml vendored Normal file
View File

@ -0,0 +1,11 @@
light:
- platform: group
name: Master Hall Lights G
entities:
- light.master_hall_lights
- light.master_hall_lights_2
- platform: group
name: Outside Patio Lights G
entities:
- light.outside_patio_lights
- light.outside_patio_lights_2

View File

@ -3,10 +3,12 @@ import logging
from os import path
from homeassistant import config
from homeassistant.const import SERVICE_RELOAD
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.reload import (
async_get_platform,
async_reload_integration_platforms,
async_setup_reload_service,
)
from tests.async_mock import Mock, patch
@ -59,5 +61,43 @@ async def test_reload_platform(hass):
assert len(setup_called) == 2
async def test_setup_reload_service(hass):
"""Test setting up a reload service."""
component_setup = Mock(return_value=True)
setup_called = []
async def setup_platform(*args):
setup_called.append(args)
mock_integration(hass, MockModule(DOMAIN, setup=component_setup))
mock_integration(hass, MockModule(PLATFORM, dependencies=[DOMAIN]))
mock_platform = MockPlatform(async_setup_platform=setup_platform)
mock_entity_platform(hass, f"{DOMAIN}.{PLATFORM}", mock_platform)
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({DOMAIN: {"platform": PLATFORM, "sensors": None}})
await hass.async_block_till_done()
assert component_setup.called
assert f"{DOMAIN}.{PLATFORM}" in hass.config.components
assert len(setup_called) == 1
await async_setup_reload_service(hass, PLATFORM, [DOMAIN])
yaml_path = path.join(
_get_fixtures_base_path(), "fixtures", "helpers/reload_configuration.yaml",
)
with patch.object(config, "YAML_CONFIG_FILE", yaml_path):
await hass.services.async_call(
PLATFORM, SERVICE_RELOAD, {}, blocking=True,
)
await hass.async_block_till_done()
assert len(setup_called) == 2
def _get_fixtures_base_path():
return path.dirname(path.dirname(__file__))