diff --git a/homeassistant/components/input_datetime/__init__.py b/homeassistant/components/input_datetime/__init__.py index 36180ed2bad..654f3547ad6 100644 --- a/homeassistant/components/input_datetime/__init__.py +++ b/homeassistant/components/input_datetime/__init__.py @@ -1,16 +1,22 @@ """Support to select a date and/or a time.""" -import logging import datetime +import logging import voluptuous as vol -from homeassistant.const import ATTR_DATE, ATTR_TIME, CONF_ICON, CONF_NAME +from homeassistant.const import ( + ATTR_DATE, + ATTR_TIME, + CONF_ICON, + CONF_NAME, + SERVICE_RELOAD, +) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.restore_state import RestoreEntity +import homeassistant.helpers.service from homeassistant.util import dt as dt_util - _LOGGER = logging.getLogger(__name__) DOMAIN = "input_datetime" @@ -52,26 +58,31 @@ CONFIG_SCHEMA = vol.Schema( }, extra=vol.ALLOW_EXTRA, ) +RELOAD_SERVICE_SCHEMA = vol.Schema({}) async def async_setup(hass, config): """Set up an input datetime.""" component = EntityComponent(_LOGGER, DOMAIN, hass) - entities = [] + entities = await _async_process_config(config) - for object_id, cfg in config[DOMAIN].items(): - name = cfg.get(CONF_NAME) - has_time = cfg.get(CONF_HAS_TIME) - has_date = cfg.get(CONF_HAS_DATE) - icon = cfg.get(CONF_ICON) - initial = cfg.get(CONF_INITIAL) - entities.append( - InputDatetime(object_id, name, has_date, has_time, icon, initial) - ) + async def reload_service_handler(service_call): + """Remove all entities and load new ones from config.""" + conf = await component.async_prepare_reload() + if conf is None: + return + new_entities = await _async_process_config(conf) + if new_entities: + await component.async_add_entities(new_entities) - if not entities: - return False + homeassistant.helpers.service.async_register_admin_service( + hass, + DOMAIN, + SERVICE_RELOAD, + reload_service_handler, + schema=RELOAD_SERVICE_SCHEMA, + ) async def async_set_datetime_service(entity, call): """Handle a call to the input datetime 'set datetime' service.""" @@ -108,10 +119,28 @@ async def async_setup(hass, config): async_set_datetime_service, ) - await component.async_add_entities(entities) + if entities: + await component.async_add_entities(entities) return True +async def _async_process_config(config): + """Process config and create list of entities.""" + entities = [] + + for object_id, cfg in config[DOMAIN].items(): + name = cfg.get(CONF_NAME) + has_time = cfg.get(CONF_HAS_TIME) + has_date = cfg.get(CONF_HAS_DATE) + icon = cfg.get(CONF_ICON) + initial = cfg.get(CONF_INITIAL) + entities.append( + InputDatetime(object_id, name, has_date, has_time, icon, initial) + ) + + return entities + + class InputDatetime(RestoreEntity): """Representation of a datetime input.""" diff --git a/homeassistant/components/input_datetime/services.yaml b/homeassistant/components/input_datetime/services.yaml index 8a40be47acd..472bd1b83b9 100644 --- a/homeassistant/components/input_datetime/services.yaml +++ b/homeassistant/components/input_datetime/services.yaml @@ -9,3 +9,6 @@ set_datetime: example: '"time": "05:30:00"'} datetime: {description: The target date & time the entity should be set to. Do not use with date or time., example: '"datetime": "2019-04-22 05:30:00"'} + +reload: + description: Reload the input_datetime configuration. diff --git a/tests/components/input_datetime/test_init.py b/tests/components/input_datetime/test_init.py index 2ddeddbefac..427433e22d2 100644 --- a/tests/components/input_datetime/test_init.py +++ b/tests/components/input_datetime/test_init.py @@ -2,20 +2,23 @@ # pylint: disable=protected-access import asyncio import datetime +from unittest.mock import patch import pytest import voluptuous as vol -from homeassistant.core import CoreState, State, Context -from homeassistant.setup import async_setup_component from homeassistant.components.input_datetime import ( - DOMAIN, ATTR_DATE, ATTR_DATETIME, ATTR_TIME, + DOMAIN, + SERVICE_RELOAD, SERVICE_SET_DATETIME, ) from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import Context, CoreState, State +from homeassistant.exceptions import Unauthorized +from homeassistant.setup import async_setup_component from tests.common import mock_restore_cache @@ -310,3 +313,65 @@ async def test_input_datetime_context(hass, hass_admin_user): assert state2 is not None assert state.state != state2.state assert state2.context.user_id == hass_admin_user.id + + +async def test_reload(hass, hass_admin_user, hass_read_only_user): + """Test reload service.""" + count_start = len(hass.states.async_entity_ids()) + + assert await async_setup_component( + hass, + DOMAIN, + { + DOMAIN: { + "dt1": {"has_time": False, "has_date": True, "initial": "2019-1-1"}, + } + }, + ) + + assert count_start + 1 == len(hass.states.async_entity_ids()) + + state_1 = hass.states.get("input_datetime.dt1") + state_2 = hass.states.get("input_datetime.dt2") + + dt_obj = datetime.datetime(2019, 1, 1, 0, 0) + assert state_1 is not None + assert state_2 is None + assert str(dt_obj.date()) == state_1.state + + with patch( + "homeassistant.config.load_yaml_config_file", + autospec=True, + return_value={ + DOMAIN: { + "dt1": {"has_time": True, "has_date": False, "initial": "23:32"}, + "dt2": {"has_time": True, "has_date": True}, + } + }, + ): + with patch("homeassistant.config.find_config_file", return_value=""): + with pytest.raises(Unauthorized): + await hass.services.async_call( + DOMAIN, + SERVICE_RELOAD, + blocking=True, + context=Context(user_id=hass_read_only_user.id), + ) + await hass.services.async_call( + DOMAIN, + SERVICE_RELOAD, + blocking=True, + context=Context(user_id=hass_admin_user.id), + ) + await hass.async_block_till_done() + + assert count_start + 2 == len(hass.states.async_entity_ids()) + + state_1 = hass.states.get("input_datetime.dt1") + state_2 = hass.states.get("input_datetime.dt2") + + dt_obj = datetime.datetime(2019, 1, 1, 23, 32) + assert state_1 is not None + assert state_2 is not None + assert str(dt_obj.time()) == state_1.state + assert str(datetime.datetime(1970, 1, 1, 0, 0)) == state_2.state