Fix async probs (#9924)
* Update entity.py * Update entity_component.py * Update entity_component.py * Update __init__.py * Update entity_component.py * Update entity_component.py * Update entity.py * cleanup entity * Update entity_component.py * Update entity_component.py * Fix names & comments / fix tests * Revert deadlock protection * Add tests for entity * Add test fix name * Update other code * Fix lint * Remove restore state from template entities * Lint
This commit is contained in:
parent
6cce934f72
commit
c1b197419d
24 changed files with 356 additions and 362 deletions
|
@ -124,20 +124,13 @@ def async_setup(hass, config):
|
|||
|
||||
method = "async_{}".format(SERVICE_TO_METHOD[service.service])
|
||||
|
||||
update_tasks = []
|
||||
for alarm in target_alarms:
|
||||
yield from getattr(alarm, method)(code)
|
||||
|
||||
update_tasks = []
|
||||
for alarm in target_alarms:
|
||||
if not alarm.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = hass.async_add_job(
|
||||
alarm.async_update_ha_state(True))
|
||||
if hasattr(alarm, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
update_tasks.append(alarm.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
|
|
@ -15,13 +15,12 @@ from homeassistant.components.binary_sensor import (
|
|||
DEVICE_CLASSES_SCHEMA)
|
||||
from homeassistant.const import (
|
||||
ATTR_FRIENDLY_NAME, ATTR_ENTITY_ID, CONF_VALUE_TEMPLATE,
|
||||
CONF_SENSORS, CONF_DEVICE_CLASS, EVENT_HOMEASSISTANT_START, STATE_ON)
|
||||
CONF_SENSORS, CONF_DEVICE_CLASS, EVENT_HOMEASSISTANT_START)
|
||||
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_track_state_change, async_track_same_state)
|
||||
from homeassistant.helpers.restore_state import async_get_last_state
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -94,10 +93,6 @@ class BinarySensorTemplate(BinarySensorDevice):
|
|||
@asyncio.coroutine
|
||||
def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
state = yield from async_get_last_state(self.hass, self.entity_id)
|
||||
if state:
|
||||
self._state = state.state == STATE_ON
|
||||
|
||||
@callback
|
||||
def template_bsensor_state_listener(entity, old_state, new_state):
|
||||
"""Handle the target device state changes."""
|
||||
|
|
|
@ -126,23 +126,16 @@ def async_setup(hass, config):
|
|||
"""Handle calls to the camera services."""
|
||||
target_cameras = component.async_extract_from_service(service)
|
||||
|
||||
update_tasks = []
|
||||
for camera in target_cameras:
|
||||
if service.service == SERVICE_EN_MOTION:
|
||||
yield from camera.async_enable_motion_detection()
|
||||
elif service.service == SERVICE_DISEN_MOTION:
|
||||
yield from camera.async_disable_motion_detection()
|
||||
|
||||
update_tasks = []
|
||||
for camera in target_cameras:
|
||||
if not camera.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = hass.async_add_job(
|
||||
camera.async_update_ha_state(True))
|
||||
if hasattr(camera, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
update_tasks.append(camera.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
|
|
@ -236,24 +236,6 @@ def async_setup(hass, config):
|
|||
load_yaml_config_file,
|
||||
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _async_update_climate(target_climate):
|
||||
"""Update climate entity after service stuff."""
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = hass.async_add_job(
|
||||
climate.async_update_ha_state(True))
|
||||
if hasattr(climate, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_away_mode_set_service(service):
|
||||
"""Set away mode on target climate devices."""
|
||||
|
@ -261,13 +243,19 @@ def async_setup(hass, config):
|
|||
|
||||
away_mode = service.data.get(ATTR_AWAY_MODE)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
if away_mode:
|
||||
yield from climate.async_turn_away_mode_on()
|
||||
else:
|
||||
yield from climate.async_turn_away_mode_off()
|
||||
|
||||
yield from _async_update_climate(target_climate)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_AWAY_MODE, async_away_mode_set_service,
|
||||
|
@ -281,10 +269,16 @@ def async_setup(hass, config):
|
|||
|
||||
hold_mode = service.data.get(ATTR_HOLD_MODE)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
yield from climate.async_set_hold_mode(hold_mode)
|
||||
|
||||
yield from _async_update_climate(target_climate)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_HOLD_MODE, async_hold_mode_set_service,
|
||||
|
@ -298,13 +292,19 @@ def async_setup(hass, config):
|
|||
|
||||
aux_heat = service.data.get(ATTR_AUX_HEAT)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
if aux_heat:
|
||||
yield from climate.async_turn_aux_heat_on()
|
||||
else:
|
||||
yield from climate.async_turn_aux_heat_off()
|
||||
|
||||
yield from _async_update_climate(target_climate)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_AUX_HEAT, async_aux_heat_set_service,
|
||||
|
@ -316,6 +316,7 @@ def async_setup(hass, config):
|
|||
"""Set temperature on the target climate devices."""
|
||||
target_climate = component.async_extract_from_service(service)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
kwargs = {}
|
||||
for value, temp in service.data.items():
|
||||
|
@ -330,7 +331,12 @@ def async_setup(hass, config):
|
|||
|
||||
yield from climate.async_set_temperature(**kwargs)
|
||||
|
||||
yield from _async_update_climate(target_climate)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_TEMPERATURE, async_temperature_set_service,
|
||||
|
@ -344,10 +350,15 @@ def async_setup(hass, config):
|
|||
|
||||
humidity = service.data.get(ATTR_HUMIDITY)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
yield from climate.async_set_humidity(humidity)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
yield from _async_update_climate(target_climate)
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_HUMIDITY, async_humidity_set_service,
|
||||
|
@ -361,10 +372,15 @@ def async_setup(hass, config):
|
|||
|
||||
fan = service.data.get(ATTR_FAN_MODE)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
yield from climate.async_set_fan_mode(fan)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
yield from _async_update_climate(target_climate)
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_FAN_MODE, async_fan_mode_set_service,
|
||||
|
@ -378,10 +394,15 @@ def async_setup(hass, config):
|
|||
|
||||
operation_mode = service.data.get(ATTR_OPERATION_MODE)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
yield from climate.async_set_operation_mode(operation_mode)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
yield from _async_update_climate(target_climate)
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_OPERATION_MODE, async_operation_set_service,
|
||||
|
@ -395,10 +416,15 @@ def async_setup(hass, config):
|
|||
|
||||
swing_mode = service.data.get(ATTR_SWING_MODE)
|
||||
|
||||
update_tasks = []
|
||||
for climate in target_climate:
|
||||
yield from climate.async_set_swing_mode(swing_mode)
|
||||
if not climate.should_poll:
|
||||
continue
|
||||
update_tasks.append(climate.async_update_ha_state(True))
|
||||
|
||||
yield from _async_update_climate(target_climate)
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_SET_SWING_MODE, async_swing_set_service,
|
||||
|
|
|
@ -169,21 +169,12 @@ def async_setup(hass, config):
|
|||
params.pop(ATTR_ENTITY_ID, None)
|
||||
|
||||
# call method
|
||||
update_tasks = []
|
||||
for cover in covers:
|
||||
yield from getattr(cover, method['method'])(**params)
|
||||
|
||||
update_tasks = []
|
||||
|
||||
for cover in covers:
|
||||
if not cover.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = hass.async_add_job(
|
||||
cover.async_update_ha_state(True))
|
||||
if hasattr(cover, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
update_tasks.append(cover.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
|
|
@ -24,7 +24,6 @@ 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_track_state_change
|
||||
from homeassistant.helpers.restore_state import async_get_last_state
|
||||
from homeassistant.helpers.script import Script
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -134,7 +133,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
_LOGGER.error("No covers added")
|
||||
return False
|
||||
|
||||
async_add_devices(covers, True)
|
||||
async_add_devices(covers)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -190,10 +189,6 @@ class CoverTemplate(CoverDevice):
|
|||
@asyncio.coroutine
|
||||
def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
state = yield from async_get_last_state(self.hass, self.entity_id)
|
||||
if state:
|
||||
self._position = 100 if state.state == STATE_OPEN else 0
|
||||
|
||||
@callback
|
||||
def template_cover_state_listener(entity, old_state, new_state):
|
||||
"""Handle target device state changes."""
|
||||
|
|
|
@ -215,20 +215,12 @@ def async_setup(hass, config: dict):
|
|||
target_fans = component.async_extract_from_service(service)
|
||||
params.pop(ATTR_ENTITY_ID, None)
|
||||
|
||||
update_tasks = []
|
||||
for fan in target_fans:
|
||||
yield from getattr(fan, method['method'])(**params)
|
||||
|
||||
update_tasks = []
|
||||
|
||||
for fan in target_fans:
|
||||
if not fan.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = hass.async_add_job(fan.async_update_ha_state(True))
|
||||
if hasattr(fan, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
update_tasks.append(fan.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
|
|
@ -274,6 +274,7 @@ def async_setup(hass, config):
|
|||
|
||||
preprocess_turn_on_alternatives(params)
|
||||
|
||||
update_tasks = []
|
||||
for light in target_lights:
|
||||
if service.service == SERVICE_TURN_ON:
|
||||
yield from light.async_turn_on(**params)
|
||||
|
@ -282,18 +283,9 @@ def async_setup(hass, config):
|
|||
else:
|
||||
yield from light.async_toggle(**params)
|
||||
|
||||
update_tasks = []
|
||||
|
||||
for light in target_lights:
|
||||
if not light.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = hass.async_add_job(
|
||||
light.async_update_ha_state(True))
|
||||
if hasattr(light, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
update_tasks.append(light.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
|
|
@ -20,7 +20,6 @@ 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_track_state_change
|
||||
from homeassistant.helpers.restore_state import async_get_last_state
|
||||
from homeassistant.helpers.script import Script
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -87,7 +86,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
_LOGGER.error("No lights added")
|
||||
return False
|
||||
|
||||
async_add_devices(lights, True)
|
||||
async_add_devices(lights)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -150,10 +149,6 @@ class LightTemplate(Light):
|
|||
@asyncio.coroutine
|
||||
def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
state = yield from async_get_last_state(self.hass, self.entity_id)
|
||||
if state:
|
||||
self._state = state.state == STATE_ON
|
||||
|
||||
@callback
|
||||
def template_light_state_listener(entity, old_state, new_state):
|
||||
"""Handle target device state changes."""
|
||||
|
@ -207,6 +202,7 @@ class LightTemplate(Light):
|
|||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Update the state from the template."""
|
||||
print("ASYNC UPDATE")
|
||||
if self._template is not None:
|
||||
try:
|
||||
state = self._template.async_render().lower()
|
||||
|
|
|
@ -90,24 +90,16 @@ def async_setup(hass, config):
|
|||
|
||||
code = service.data.get(ATTR_CODE)
|
||||
|
||||
update_tasks = []
|
||||
for entity in target_locks:
|
||||
if service.service == SERVICE_LOCK:
|
||||
yield from entity.async_lock(code=code)
|
||||
else:
|
||||
yield from entity.async_unlock(code=code)
|
||||
|
||||
update_tasks = []
|
||||
|
||||
for entity in target_locks:
|
||||
if not entity.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = hass.async_add_job(
|
||||
entity.async_update_ha_state(True))
|
||||
if hasattr(entity, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
update_tasks.append(entity.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
|
|
@ -406,16 +406,9 @@ def async_setup(hass, config):
|
|||
update_tasks = []
|
||||
for player in target_players:
|
||||
yield from getattr(player, method['method'])(**params)
|
||||
|
||||
for player in target_players:
|
||||
if not player.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = player.async_update_ha_state(True)
|
||||
if hasattr(player, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
update_tasks.append(player.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
|
|
@ -148,6 +148,7 @@ def async_setup(hass, config):
|
|||
num_repeats = service.data.get(ATTR_NUM_REPEATS)
|
||||
delay_secs = service.data.get(ATTR_DELAY_SECS)
|
||||
|
||||
update_tasks = []
|
||||
for remote in target_remotes:
|
||||
if service.service == SERVICE_TURN_ON:
|
||||
yield from remote.async_turn_on(activity=activity_id)
|
||||
|
@ -160,17 +161,9 @@ def async_setup(hass, config):
|
|||
else:
|
||||
yield from remote.async_turn_off(activity=activity_id)
|
||||
|
||||
update_tasks = []
|
||||
for remote in target_remotes:
|
||||
if not remote.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = hass.async_add_job(
|
||||
remote.async_update_ha_state(True))
|
||||
if hasattr(remote, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
update_tasks.append(remote.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
|
|
@ -19,7 +19,6 @@ 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.event import async_track_state_change
|
||||
from homeassistant.helpers.restore_state import async_get_last_state
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -93,10 +92,6 @@ class SensorTemplate(Entity):
|
|||
@asyncio.coroutine
|
||||
def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
state = yield from async_get_last_state(self.hass, self.entity_id)
|
||||
if state:
|
||||
self._state = state.state
|
||||
|
||||
@callback
|
||||
def template_sensor_state_listener(entity, old_state, new_state):
|
||||
"""Handle device state changes."""
|
||||
|
|
|
@ -107,6 +107,7 @@ def async_setup(hass, config):
|
|||
"""Handle calls to the switch services."""
|
||||
target_switches = component.async_extract_from_service(service)
|
||||
|
||||
update_tasks = []
|
||||
for switch in target_switches:
|
||||
if service.service == SERVICE_TURN_ON:
|
||||
yield from switch.async_turn_on()
|
||||
|
@ -115,17 +116,9 @@ def async_setup(hass, config):
|
|||
else:
|
||||
yield from switch.async_turn_off()
|
||||
|
||||
update_tasks = []
|
||||
for switch in target_switches:
|
||||
if not switch.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = hass.async_add_job(
|
||||
switch.async_update_ha_state(True))
|
||||
if hasattr(switch, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
update_tasks.append(switch.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
|
|
@ -19,7 +19,6 @@ 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_track_state_change
|
||||
from homeassistant.helpers.restore_state import async_get_last_state
|
||||
from homeassistant.helpers.script import Script
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -71,7 +70,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||
_LOGGER.error("No switches added")
|
||||
return False
|
||||
|
||||
async_add_devices(switches, True)
|
||||
async_add_devices(switches)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -96,10 +95,6 @@ class SwitchTemplate(SwitchDevice):
|
|||
@asyncio.coroutine
|
||||
def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
state = yield from async_get_last_state(self.hass, self.entity_id)
|
||||
if state:
|
||||
self._state = state.state == STATE_ON
|
||||
|
||||
@callback
|
||||
def template_switch_state_listener(entity, old_state, new_state):
|
||||
"""Handle target device state changes."""
|
||||
|
|
|
@ -200,13 +200,7 @@ def async_setup(hass, config):
|
|||
yield from getattr(vacuum, method['method'])(**params)
|
||||
if not vacuum.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = hass.async_add_job(
|
||||
vacuum.async_update_ha_state(True))
|
||||
if hasattr(vacuum, 'async_update'):
|
||||
update_tasks.append(update_coro)
|
||||
else:
|
||||
yield from update_coro
|
||||
update_tasks.append(vacuum.async_update_ha_state(True))
|
||||
|
||||
if update_tasks:
|
||||
yield from asyncio.wait(update_tasks, loop=hass.loop)
|
||||
|
|
|
@ -71,8 +71,11 @@ class Entity(object):
|
|||
# If we reported if this entity was slow
|
||||
_slow_reported = False
|
||||
|
||||
# protect for multiple updates
|
||||
_update_warn = None
|
||||
# Protect for multiple updates
|
||||
_update_staged = False
|
||||
|
||||
# Process updates pararell
|
||||
parallel_updates = None
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
|
@ -197,11 +200,15 @@ class Entity(object):
|
|||
|
||||
# update entity data
|
||||
if force_refresh:
|
||||
if self._update_warn:
|
||||
# Update is already in progress.
|
||||
if self._update_staged:
|
||||
return
|
||||
self._update_staged = True
|
||||
|
||||
self._update_warn = self.hass.loop.call_later(
|
||||
# Process update sequential
|
||||
if self.parallel_updates:
|
||||
yield from self.parallel_updates.acquire()
|
||||
|
||||
update_warn = self.hass.loop.call_later(
|
||||
SLOW_UPDATE_WARNING, _LOGGER.warning,
|
||||
"Update of %s is taking over %s seconds", self.entity_id,
|
||||
SLOW_UPDATE_WARNING
|
||||
|
@ -217,8 +224,10 @@ class Entity(object):
|
|||
_LOGGER.exception("Update for %s fails", self.entity_id)
|
||||
return
|
||||
finally:
|
||||
self._update_warn.cancel()
|
||||
self._update_warn = None
|
||||
self._update_staged = False
|
||||
update_warn.cancel()
|
||||
if self.parallel_updates:
|
||||
self.parallel_updates.release()
|
||||
|
||||
start = timer()
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ class EntityComponent(object):
|
|||
self.config = None
|
||||
|
||||
self._platforms = {
|
||||
'core': EntityPlatform(self, domain, self.scan_interval, None),
|
||||
'core': EntityPlatform(self, domain, self.scan_interval, 0, None),
|
||||
}
|
||||
self.async_add_entities = self._platforms['core'].async_add_entities
|
||||
self.add_entities = self._platforms['core'].add_entities
|
||||
|
@ -128,16 +128,22 @@ class EntityComponent(object):
|
|||
return
|
||||
|
||||
# Config > Platform > Component
|
||||
scan_interval = (platform_config.get(CONF_SCAN_INTERVAL) or
|
||||
getattr(platform, 'SCAN_INTERVAL', None) or
|
||||
self.scan_interval)
|
||||
scan_interval = (
|
||||
platform_config.get(CONF_SCAN_INTERVAL) or
|
||||
getattr(platform, 'SCAN_INTERVAL', None) or self.scan_interval)
|
||||
parallel_updates = getattr(
|
||||
platform, 'PARALLEL_UPDATES',
|
||||
int(not hasattr(platform, 'async_setup_platform')))
|
||||
|
||||
entity_namespace = platform_config.get(CONF_ENTITY_NAMESPACE)
|
||||
|
||||
key = (platform_type, scan_interval, entity_namespace)
|
||||
|
||||
if key not in self._platforms:
|
||||
self._platforms[key] = EntityPlatform(
|
||||
self, platform_type, scan_interval, entity_namespace)
|
||||
entity_platform = self._platforms[key] = EntityPlatform(
|
||||
self, platform_type, scan_interval, parallel_updates,
|
||||
entity_namespace)
|
||||
else:
|
||||
entity_platform = self._platforms[key]
|
||||
|
||||
self.logger.info("Setting up %s.%s", self.domain, platform_type)
|
||||
|
@ -204,13 +210,6 @@ class EntityComponent(object):
|
|||
|
||||
entity.hass = self.hass
|
||||
|
||||
# update/init entity data
|
||||
if update_before_add:
|
||||
if hasattr(entity, 'async_update'):
|
||||
yield from entity.async_update()
|
||||
else:
|
||||
yield from self.hass.async_add_job(entity.update)
|
||||
|
||||
if getattr(entity, 'entity_id', None) is None:
|
||||
object_id = entity.name or DEVICE_DEFAULT_NAME
|
||||
|
||||
|
@ -235,7 +234,7 @@ class EntityComponent(object):
|
|||
if hasattr(entity, 'async_added_to_hass'):
|
||||
yield from entity.async_added_to_hass()
|
||||
|
||||
yield from entity.async_update_ha_state()
|
||||
yield from entity.async_update_ha_state(update_before_add)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -316,17 +315,23 @@ class EntityComponent(object):
|
|||
class EntityPlatform(object):
|
||||
"""Keep track of entities for a single platform and stay in loop."""
|
||||
|
||||
def __init__(self, component, platform, scan_interval, entity_namespace):
|
||||
def __init__(self, component, platform, scan_interval, parallel_updates,
|
||||
entity_namespace):
|
||||
"""Initialize the entity platform."""
|
||||
self.component = component
|
||||
self.platform = platform
|
||||
self.scan_interval = scan_interval
|
||||
self.parallel_updates = None
|
||||
self.entity_namespace = entity_namespace
|
||||
self.platform_entities = []
|
||||
self._tasks = []
|
||||
self._async_unsub_polling = None
|
||||
self._process_updates = asyncio.Lock(loop=component.hass.loop)
|
||||
|
||||
if parallel_updates:
|
||||
self.parallel_updates = asyncio.Semaphore(
|
||||
parallel_updates, loop=component.hass.loop)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_block_entities_done(self):
|
||||
"""Wait until all entities add to hass."""
|
||||
|
@ -377,6 +382,7 @@ class EntityPlatform(object):
|
|||
@asyncio.coroutine
|
||||
def async_process_entity(new_entity):
|
||||
"""Add entities to StateMachine."""
|
||||
new_entity.parallel_updates = self.parallel_updates
|
||||
ret = yield from self.component.async_add_entity(
|
||||
new_entity, self, update_before_add=update_before_add
|
||||
)
|
||||
|
@ -432,26 +438,10 @@ class EntityPlatform(object):
|
|||
|
||||
with (yield from self._process_updates):
|
||||
tasks = []
|
||||
to_update = []
|
||||
|
||||
for entity in self.platform_entities:
|
||||
if not entity.should_poll:
|
||||
continue
|
||||
|
||||
update_coro = entity.async_update_ha_state(True)
|
||||
if hasattr(entity, 'async_update'):
|
||||
tasks.append(
|
||||
self.component.hass.async_add_job(update_coro))
|
||||
else:
|
||||
to_update.append(update_coro)
|
||||
|
||||
for update_coro in to_update:
|
||||
try:
|
||||
yield from update_coro
|
||||
except Exception: # pylint: disable=broad-except
|
||||
self.component.logger.exception(
|
||||
"Error while update entity from %s in %s",
|
||||
self.platform, self.component.domain)
|
||||
tasks.append(entity.async_update_ha_state(True))
|
||||
|
||||
if tasks:
|
||||
yield from asyncio.wait(tasks, loop=self.component.hass.loop)
|
||||
|
|
|
@ -4,7 +4,6 @@ from datetime import timedelta
|
|||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
from homeassistant.core import CoreState, State
|
||||
from homeassistant.const import MATCH_ALL
|
||||
from homeassistant import setup
|
||||
from homeassistant.components.binary_sensor import template
|
||||
|
@ -12,11 +11,9 @@ from homeassistant.exceptions import TemplateError
|
|||
from homeassistant.helpers import template as template_hlpr
|
||||
from homeassistant.util.async import run_callback_threadsafe
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, mock_component,
|
||||
async_fire_time_changed)
|
||||
get_test_home_assistant, assert_setup_component, async_fire_time_changed)
|
||||
|
||||
|
||||
class TestBinarySensorTemplate(unittest.TestCase):
|
||||
|
@ -169,41 +166,6 @@ class TestBinarySensorTemplate(unittest.TestCase):
|
|||
run_callback_threadsafe(self.hass.loop, vs.async_check_state).result()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_restore_state(hass):
|
||||
"""Ensure states are restored on startup."""
|
||||
hass.data[DATA_RESTORE_CACHE] = {
|
||||
'binary_sensor.test': State('binary_sensor.test', 'on'),
|
||||
}
|
||||
|
||||
hass.state = CoreState.starting
|
||||
mock_component(hass, 'recorder')
|
||||
|
||||
config = {
|
||||
'binary_sensor': {
|
||||
'platform': 'template',
|
||||
'sensors': {
|
||||
'test': {
|
||||
'friendly_name': 'virtual thingy',
|
||||
'value_template':
|
||||
"{{ states.sensor.test_state.state == 'on' }}",
|
||||
'device_class': 'motion',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
yield from setup.async_setup_component(hass, 'binary_sensor', config)
|
||||
|
||||
state = hass.states.get('binary_sensor.test')
|
||||
assert state.state == 'on'
|
||||
|
||||
yield from hass.async_start()
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get('binary_sensor.test')
|
||||
assert state.state == 'off'
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_template_delay_on(hass):
|
||||
"""Test binary sensor template delay on."""
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
"""The tests for the Template light platform."""
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from homeassistant.core import callback, State, CoreState
|
||||
from homeassistant.core import callback
|
||||
from homeassistant import setup
|
||||
import homeassistant.components as core
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS
|
||||
from homeassistant.const import STATE_ON, STATE_OFF
|
||||
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, mock_component)
|
||||
get_test_home_assistant, assert_setup_component)
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -627,49 +625,3 @@ class TestTemplateLight:
|
|||
assert state is not None
|
||||
|
||||
assert state.attributes.get('friendly_name') == 'Template light'
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_restore_state(hass):
|
||||
"""Ensure states are restored on startup."""
|
||||
hass.data[DATA_RESTORE_CACHE] = {
|
||||
'light.test_template_light':
|
||||
State('light.test_template_light', 'on'),
|
||||
}
|
||||
|
||||
hass.state = CoreState.starting
|
||||
mock_component(hass, 'recorder')
|
||||
yield from setup.async_setup_component(hass, 'light', {
|
||||
'light': {
|
||||
'platform': 'template',
|
||||
'lights': {
|
||||
'test_template_light': {
|
||||
'value_template':
|
||||
"{{states.light.test_state.state}}",
|
||||
'turn_on': {
|
||||
'service': 'test.automation',
|
||||
},
|
||||
'turn_off': {
|
||||
'service': 'light.turn_off',
|
||||
'entity_id': 'light.test_state'
|
||||
},
|
||||
'set_level': {
|
||||
'service': 'test.automation',
|
||||
'data_template': {
|
||||
'entity_id': 'light.test_state',
|
||||
'brightness': '{{brightness}}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
state = hass.states.get('light.test_template_light')
|
||||
assert state.state == 'on'
|
||||
|
||||
yield from hass.async_start()
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get('light.test_template_light')
|
||||
assert state.state == 'off'
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
"""The test for the Template sensor platform."""
|
||||
import asyncio
|
||||
from homeassistant.setup import setup_component
|
||||
|
||||
from homeassistant.core import CoreState, State
|
||||
from homeassistant.setup import setup_component, async_setup_component
|
||||
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, mock_component)
|
||||
from tests.common import get_test_home_assistant, assert_setup_component
|
||||
|
||||
|
||||
class TestTemplateSensor:
|
||||
|
@ -188,36 +183,3 @@ class TestTemplateSensor:
|
|||
self.hass.block_till_done()
|
||||
|
||||
assert self.hass.states.all() == []
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_restore_state(hass):
|
||||
"""Ensure states are restored on startup."""
|
||||
hass.data[DATA_RESTORE_CACHE] = {
|
||||
'sensor.test_template_sensor':
|
||||
State('sensor.test_template_sensor', 'It Test.'),
|
||||
}
|
||||
|
||||
hass.state = CoreState.starting
|
||||
mock_component(hass, 'recorder')
|
||||
|
||||
yield from async_setup_component(hass, 'sensor', {
|
||||
'sensor': {
|
||||
'platform': 'template',
|
||||
'sensors': {
|
||||
'test_template_sensor': {
|
||||
'value_template':
|
||||
"It {{ states.sensor.test_state.state }}."
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
state = hass.states.get('sensor.test_template_sensor')
|
||||
assert state.state == 'It Test.'
|
||||
|
||||
yield from hass.async_start()
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get('sensor.test_template_sensor')
|
||||
assert state.state == 'It .'
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
"""The tests for the Template switch platform."""
|
||||
import asyncio
|
||||
|
||||
from homeassistant.core import callback, State, CoreState
|
||||
from homeassistant.core import callback
|
||||
from homeassistant import setup
|
||||
import homeassistant.components as core
|
||||
from homeassistant.const import STATE_ON, STATE_OFF
|
||||
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, mock_component)
|
||||
get_test_home_assistant, assert_setup_component)
|
||||
|
||||
|
||||
class TestTemplateSwitch:
|
||||
|
@ -410,44 +407,3 @@ class TestTemplateSwitch:
|
|||
self.hass.block_till_done()
|
||||
|
||||
assert len(self.calls) == 1
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_restore_state(hass):
|
||||
"""Ensure states are restored on startup."""
|
||||
hass.data[DATA_RESTORE_CACHE] = {
|
||||
'switch.test_template_switch':
|
||||
State('switch.test_template_switch', 'on'),
|
||||
}
|
||||
|
||||
hass.state = CoreState.starting
|
||||
mock_component(hass, 'recorder')
|
||||
|
||||
yield from setup.async_setup_component(hass, 'switch', {
|
||||
'switch': {
|
||||
'platform': 'template',
|
||||
'switches': {
|
||||
'test_template_switch': {
|
||||
'value_template':
|
||||
"{{ states.switch.test_state.state }}",
|
||||
'turn_on': {
|
||||
'service': 'switch.turn_on',
|
||||
'entity_id': 'switch.test_state'
|
||||
},
|
||||
'turn_off': {
|
||||
'service': 'switch.turn_off',
|
||||
'entity_id': 'switch.test_state'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
state = hass.states.get('switch.test_template_switch')
|
||||
assert state.state == 'on'
|
||||
|
||||
yield from hass.async_start()
|
||||
yield from hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get('switch.test_template_switch')
|
||||
assert state.state == 'unavailable'
|
||||
|
|
|
@ -213,3 +213,162 @@ def test_async_schedule_update_ha_state(hass):
|
|||
yield from hass.async_block_till_done()
|
||||
|
||||
assert update_call is True
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_async_pararell_updates_with_zero(hass):
|
||||
"""Test pararell updates with 0 (disabled)."""
|
||||
updates = []
|
||||
test_lock = asyncio.Event(loop=hass.loop)
|
||||
|
||||
class AsyncEntity(entity.Entity):
|
||||
|
||||
def __init__(self, entity_id, count):
|
||||
"""Initialize Async test entity."""
|
||||
self.entity_id = entity_id
|
||||
self.hass = hass
|
||||
self._count = count
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Test update."""
|
||||
updates.append(self._count)
|
||||
yield from test_lock.wait()
|
||||
|
||||
ent_1 = AsyncEntity("sensor.test_1", 1)
|
||||
ent_2 = AsyncEntity("sensor.test_2", 2)
|
||||
|
||||
ent_1.async_schedule_update_ha_state(True)
|
||||
ent_2.async_schedule_update_ha_state(True)
|
||||
|
||||
while True:
|
||||
if len(updates) == 2:
|
||||
break
|
||||
yield from asyncio.sleep(0, loop=hass.loop)
|
||||
|
||||
assert len(updates) == 2
|
||||
assert updates == [1, 2]
|
||||
|
||||
test_lock.set()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_async_pararell_updates_with_one(hass):
|
||||
"""Test pararell updates with 1 (sequential)."""
|
||||
updates = []
|
||||
test_lock = asyncio.Lock(loop=hass.loop)
|
||||
test_semephore = asyncio.Semaphore(1, loop=hass.loop)
|
||||
|
||||
yield from test_lock.acquire()
|
||||
|
||||
class AsyncEntity(entity.Entity):
|
||||
|
||||
def __init__(self, entity_id, count):
|
||||
"""Initialize Async test entity."""
|
||||
self.entity_id = entity_id
|
||||
self.hass = hass
|
||||
self._count = count
|
||||
self.parallel_updates = test_semephore
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Test update."""
|
||||
updates.append(self._count)
|
||||
yield from test_lock.acquire()
|
||||
|
||||
ent_1 = AsyncEntity("sensor.test_1", 1)
|
||||
ent_2 = AsyncEntity("sensor.test_2", 2)
|
||||
ent_3 = AsyncEntity("sensor.test_3", 3)
|
||||
|
||||
ent_1.async_schedule_update_ha_state(True)
|
||||
ent_2.async_schedule_update_ha_state(True)
|
||||
ent_3.async_schedule_update_ha_state(True)
|
||||
|
||||
while True:
|
||||
if len(updates) == 1:
|
||||
break
|
||||
yield from asyncio.sleep(0, loop=hass.loop)
|
||||
|
||||
assert len(updates) == 1
|
||||
assert updates == [1]
|
||||
|
||||
test_lock.release()
|
||||
|
||||
while True:
|
||||
if len(updates) == 2:
|
||||
break
|
||||
yield from asyncio.sleep(0, loop=hass.loop)
|
||||
|
||||
assert len(updates) == 2
|
||||
assert updates == [1, 2]
|
||||
|
||||
test_lock.release()
|
||||
|
||||
while True:
|
||||
if len(updates) == 3:
|
||||
break
|
||||
yield from asyncio.sleep(0, loop=hass.loop)
|
||||
|
||||
assert len(updates) == 3
|
||||
assert updates == [1, 2, 3]
|
||||
|
||||
test_lock.release()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_async_pararell_updates_with_two(hass):
|
||||
"""Test pararell updates with 2 (pararell)."""
|
||||
updates = []
|
||||
test_lock = asyncio.Lock(loop=hass.loop)
|
||||
test_semephore = asyncio.Semaphore(2, loop=hass.loop)
|
||||
|
||||
yield from test_lock.acquire()
|
||||
|
||||
class AsyncEntity(entity.Entity):
|
||||
|
||||
def __init__(self, entity_id, count):
|
||||
"""Initialize Async test entity."""
|
||||
self.entity_id = entity_id
|
||||
self.hass = hass
|
||||
self._count = count
|
||||
self.parallel_updates = test_semephore
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Test update."""
|
||||
updates.append(self._count)
|
||||
yield from test_lock.acquire()
|
||||
|
||||
ent_1 = AsyncEntity("sensor.test_1", 1)
|
||||
ent_2 = AsyncEntity("sensor.test_2", 2)
|
||||
ent_3 = AsyncEntity("sensor.test_3", 3)
|
||||
ent_4 = AsyncEntity("sensor.test_4", 4)
|
||||
|
||||
ent_1.async_schedule_update_ha_state(True)
|
||||
ent_2.async_schedule_update_ha_state(True)
|
||||
ent_3.async_schedule_update_ha_state(True)
|
||||
ent_4.async_schedule_update_ha_state(True)
|
||||
|
||||
while True:
|
||||
if len(updates) == 2:
|
||||
break
|
||||
yield from asyncio.sleep(0, loop=hass.loop)
|
||||
|
||||
assert len(updates) == 2
|
||||
assert updates == [1, 2]
|
||||
|
||||
test_lock.release()
|
||||
yield from asyncio.sleep(0, loop=hass.loop)
|
||||
test_lock.release()
|
||||
|
||||
while True:
|
||||
if len(updates) == 4:
|
||||
break
|
||||
yield from asyncio.sleep(0, loop=hass.loop)
|
||||
|
||||
assert len(updates) == 4
|
||||
assert updates == [1, 2, 3, 4]
|
||||
|
||||
test_lock.release()
|
||||
yield from asyncio.sleep(0, loop=hass.loop)
|
||||
test_lock.release()
|
||||
|
|
|
@ -578,3 +578,79 @@ def test_platform_not_ready(hass):
|
|||
yield from hass.async_block_till_done()
|
||||
assert len(platform1_setup.mock_calls) == 3
|
||||
assert 'test_domain.mod1' in hass.config.components
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_pararell_updates_async_platform(hass):
|
||||
"""Warn we log when platform setup takes a long time."""
|
||||
platform = MockPlatform()
|
||||
|
||||
@asyncio.coroutine
|
||||
def mock_update(*args, **kwargs):
|
||||
pass
|
||||
|
||||
platform.async_setup_platform = mock_update
|
||||
|
||||
loader.set_component('test_domain.platform', platform)
|
||||
|
||||
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||
component._platforms = {}
|
||||
|
||||
yield from component.async_setup({
|
||||
DOMAIN: {
|
||||
'platform': 'platform',
|
||||
}
|
||||
})
|
||||
|
||||
handle = list(component._platforms.values())[-1]
|
||||
|
||||
assert handle.parallel_updates is None
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_pararell_updates_async_platform_with_constant(hass):
|
||||
"""Warn we log when platform setup takes a long time."""
|
||||
platform = MockPlatform()
|
||||
|
||||
@asyncio.coroutine
|
||||
def mock_update(*args, **kwargs):
|
||||
pass
|
||||
|
||||
platform.async_setup_platform = mock_update
|
||||
platform.PARALLEL_UPDATES = 1
|
||||
|
||||
loader.set_component('test_domain.platform', platform)
|
||||
|
||||
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||
component._platforms = {}
|
||||
|
||||
yield from component.async_setup({
|
||||
DOMAIN: {
|
||||
'platform': 'platform',
|
||||
}
|
||||
})
|
||||
|
||||
handle = list(component._platforms.values())[-1]
|
||||
|
||||
assert handle.parallel_updates is not None
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_pararell_updates_sync_platform(hass):
|
||||
"""Warn we log when platform setup takes a long time."""
|
||||
platform = MockPlatform()
|
||||
|
||||
loader.set_component('test_domain.platform', platform)
|
||||
|
||||
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||
component._platforms = {}
|
||||
|
||||
yield from component.async_setup({
|
||||
DOMAIN: {
|
||||
'platform': 'platform',
|
||||
}
|
||||
})
|
||||
|
||||
handle = list(component._platforms.values())[-1]
|
||||
|
||||
assert handle.parallel_updates is not None
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue