Manual updates (#17278)

This commit is contained in:
Paulus Schoutsen 2018-10-09 16:54:38 +02:00 committed by Pascal Vizeli
parent cf249e3e5e
commit e903f7ffda
5 changed files with 72 additions and 1 deletions

View File

@ -12,6 +12,8 @@ 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
@ -21,11 +23,16 @@ 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__)
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_id
})
def is_on(hass, entity_id=None):
@ -133,12 +140,20 @@ async def async_setup(hass: ha.HomeAssistant, config: dict) -> Awaitable[bool]:
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."""
await hass.helpers.entity_component.async_update_entity(
call.data[ATTR_ENTITY_ID])
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."""

View File

@ -346,7 +346,7 @@ class Entity:
if hasattr(self, 'async_update'):
await self.async_update()
elif hasattr(self, 'update'):
await self.hass.async_add_job(self.update)
await self.hass.async_add_executor_job(self.update)
finally:
self._update_staged = False
if warning:

View File

@ -2,6 +2,7 @@
import asyncio
from datetime import timedelta
from itertools import chain
import logging
from homeassistant import config as conf_util
from homeassistant.setup import async_prepare_setup_platform
@ -11,10 +12,33 @@ from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_per_platform, discovery
from homeassistant.helpers.service import extract_entity_ids
from homeassistant.loader import bind_hass
from homeassistant.util import slugify
from .entity_platform import EntityPlatform
DEFAULT_SCAN_INTERVAL = timedelta(seconds=15)
DATA_INSTANCES = 'entity_components'
@bind_hass
async def async_update_entity(hass, entity_id):
"""Trigger an update for an entity."""
domain = entity_id.split('.', 1)[0]
entity_comp = hass.data.get(DATA_INSTANCES, {}).get(domain)
if entity_comp is None:
logging.getLogger(__name__).warning(
'Forced update failed. Component for %s not loaded.', entity_id)
return
entity = entity_comp.get_entity(entity_id)
if entity is None:
logging.getLogger(__name__).warning(
'Forced update failed. Entity %s not found.', entity_id)
return
await entity.async_update_ha_state(True)
class EntityComponent:
@ -45,6 +69,8 @@ class EntityComponent:
self.async_add_entities = self._platforms[domain].async_add_entities
self.add_entities = self._platforms[domain].add_entities
hass.data.setdefault(DATA_INSTANCES, {})[domain] = self
@property
def entities(self):
"""Return an iterable that returns all entities."""

View File

@ -355,3 +355,17 @@ async def test_turn_on_to_not_block_for_domains_without_service(hass):
'light', 'turn_on', {'entity_id': ['light.bla', 'light.test']}, True)
assert mock_call.call_args_list[1][0] == (
'sensor', 'turn_on', {'entity_id': ['sensor.bla']}, False)
async def test_entity_update(hass):
"""Test being able to call entity update."""
await comps.async_setup(hass, {})
with patch('homeassistant.helpers.entity_component.async_update_entity',
return_value=mock_coro()) as mock_update:
await hass.services.async_call('homeassistant', 'update_entity', {
'entity_id': 'light.kitchen'
}, blocking=True)
assert len(mock_update.mock_calls) == 1
assert mock_update.mock_calls[0][1][1] == 'light.kitchen'

View File

@ -415,3 +415,19 @@ async def test_unload_entry_fails_if_never_loaded(hass):
with pytest.raises(ValueError):
await component.async_unload_entry(entry)
async def test_update_entity(hass):
"""Test that we can update an entity with the helper."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
entity = MockEntity()
entity.async_update_ha_state = Mock(return_value=mock_coro())
await component.async_add_entities([entity])
# Called as part of async_add_entities
assert len(entity.async_update_ha_state.mock_calls) == 1
await hass.helpers.entity_component.async_update_entity(entity.entity_id)
assert len(entity.async_update_ha_state.mock_calls) == 2
assert entity.async_update_ha_state.mock_calls[-1][1][0] is True