Add transition support to scenes, cleanup blocking parameter (#34434)

This commit is contained in:
Franck Nijhof 2020-04-21 03:07:50 +02:00 committed by GitHub
parent 19be31d13a
commit bc5a2da7b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 547 additions and 229 deletions

View File

@ -1,7 +1,7 @@
"""Reproduce an Alarm control panel state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -36,7 +36,11 @@ VALID_STATES = {
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -76,9 +80,18 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Alarm control panel states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,7 +1,7 @@
"""Reproduce an Automation state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -21,7 +21,11 @@ VALID_STATES = {STATE_ON, STATE_OFF}
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -53,9 +57,18 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Automation states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,6 +1,6 @@
"""Module that groups code required to handle state restore for component."""
import asyncio
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import ATTR_TEMPERATURE
from homeassistant.core import Context, State
@ -26,7 +26,11 @@ from .const import (
async def _async_reproduce_states(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce component states."""
@ -69,9 +73,18 @@ async def _async_reproduce_states(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce component states."""
await asyncio.gather(
*(_async_reproduce_states(hass, state, context) for state in states)
*(
_async_reproduce_states(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,7 +1,7 @@
"""Reproduce an Counter state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import Context, State
@ -21,7 +21,11 @@ _LOGGER = logging.getLogger(__name__)
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -63,9 +67,18 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Counter states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,7 +1,7 @@
"""Reproduce an Cover state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.components.cover import (
ATTR_CURRENT_POSITION,
@ -33,7 +33,11 @@ VALID_STATES = {STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING}
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -61,13 +65,15 @@ async def _async_reproduce_state(
service_data = {ATTR_ENTITY_ID: state.entity_id}
service_data_tilting = {ATTR_ENTITY_ID: state.entity_id}
if cur_state.state != state.state or cur_state.attributes.get(
ATTR_CURRENT_POSITION
) != state.attributes.get(ATTR_CURRENT_POSITION):
if not (
cur_state.state == state.state
and cur_state.attributes.get(ATTR_CURRENT_POSITION)
== state.attributes.get(ATTR_CURRENT_POSITION)
):
# Open/Close
if state.state == STATE_CLOSED or state.state == STATE_CLOSING:
if state.state in [STATE_CLOSED, STATE_CLOSING]:
service = SERVICE_CLOSE_COVER
elif state.state == STATE_OPEN or state.state == STATE_OPENING:
elif state.state in [STATE_OPEN, STATE_OPENING]:
if (
ATTR_CURRENT_POSITION in cur_state.attributes
and ATTR_CURRENT_POSITION in state.attributes
@ -108,10 +114,19 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Cover states."""
# Reproduce states in parallel.
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,4 +1,6 @@
"""Support for deCONZ scenes."""
from typing import Any
from homeassistant.components.scene import Scene
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -18,10 +20,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
@callback
def async_add_scene(scenes):
"""Add scene from deCONZ."""
entities = []
for scene in scenes:
entities.append(DeconzScene(scene, gateway))
entities = [DeconzScene(scene, gateway) for scene in scenes]
async_add_entities(entities)
@ -51,7 +50,7 @@ class DeconzScene(Scene):
del self.gateway.deconz_ids[self.entity_id]
self._scene = None
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
await self._scene.async_set_state({})

View File

@ -1,4 +1,6 @@
"""Support for control of ElkM1 tasks ("macros")."""
from typing import Any
from homeassistant.components.scene import Scene
from . import ElkAttachedEntity, create_elk_entities
@ -17,6 +19,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class ElkTask(ElkAttachedEntity, Scene):
"""Elk-M1 task as scene."""
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate the task."""
self._element.activate()

View File

@ -2,7 +2,7 @@
import asyncio
import logging
from types import MappingProxyType
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -35,7 +35,11 @@ ATTRIBUTES = { # attribute: service
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -85,11 +89,20 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Fan states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,5 +1,6 @@
"""Support for Fibaro scenes."""
import logging
from typing import Any
from homeassistant.components.scene import Scene
@ -21,6 +22,6 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
class FibaroScene(FibaroDevice, Scene):
"""Representation of a Fibaro scene entity."""
def activate(self):
def activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
self.fibaro_device.start()

View File

@ -1,5 +1,5 @@
"""Module that groups code required to handle state restore for component."""
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.core import Context, State
from homeassistant.helpers.state import async_reproduce_state
@ -9,7 +9,11 @@ from . import get_entity_ids
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce component states."""
@ -27,4 +31,6 @@ async def async_reproduce_states(
context=state.context,
)
)
await async_reproduce_state(hass, states_copy, blocking=True, context=context)
await async_reproduce_state(
hass, states_copy, context=context, reproduce_options=reproduce_options
)

View File

@ -1,11 +1,12 @@
"""Allow users to set and activate scenes."""
from collections import namedtuple
import logging
from typing import List
from typing import Any, List
import voluptuous as vol
from homeassistant import config as conf_util
from homeassistant.components.light import ATTR_TRANSITION
from homeassistant.components.scene import DOMAIN as SCENE_DOMAIN, STATES, Scene
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -62,8 +63,8 @@ def _ensure_no_intersection(value):
if (
CONF_SNAPSHOT not in value
or CONF_ENTITIES not in value
or not any(
entity_id in value[CONF_SNAPSHOT] for entity_id in value[CONF_ENTITIES]
or all(
entity_id not in value[CONF_SNAPSHOT] for entity_id in value[CONF_ENTITIES]
)
):
return value
@ -123,13 +124,11 @@ def scenes_with_entity(hass: HomeAssistant, entity_id: str) -> List[str]:
platform = hass.data[DATA_PLATFORM]
results = []
for scene_entity in platform.entities.values():
if entity_id in scene_entity.scene_config.states:
results.append(scene_entity.entity_id)
return results
return [
scene_entity.entity_id
for scene_entity in platform.entities.values()
if entity_id in scene_entity.scene_config.states
]
@callback
@ -171,7 +170,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
conf = await conf_util.async_process_component_config(hass, conf, integration)
if not conf or not platform:
if not (conf and platform):
return
await platform.async_reset()
@ -189,15 +188,30 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
async def apply_service(call):
"""Apply a scene."""
reproduce_options = {}
if ATTR_TRANSITION in call.data:
reproduce_options[ATTR_TRANSITION] = call.data.get(ATTR_TRANSITION)
await async_reproduce_state(
hass, call.data[CONF_ENTITIES].values(), blocking=True, context=call.context
hass,
call.data[CONF_ENTITIES].values(),
context=call.context,
reproduce_options=reproduce_options,
)
hass.services.async_register(
SCENE_DOMAIN,
SERVICE_APPLY,
apply_service,
vol.Schema({vol.Required(CONF_ENTITIES): STATES_SCHEMA}),
vol.Schema(
{
vol.Optional(ATTR_TRANSITION): vol.All(
vol.Coerce(float), vol.Clamp(min=0, max=6553)
),
vol.Required(CONF_ENTITIES): STATES_SCHEMA,
}
),
)
async def create_service(call):
@ -289,11 +303,11 @@ class HomeAssistantScene(Scene):
attributes[CONF_ID] = unique_id
return attributes
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate scene. Try to get entities into requested state."""
await async_reproduce_state(
self.hass,
self.scene_config.states.values(),
blocking=True,
context=self._context,
reproduce_options=kwargs,
)

View File

@ -1,5 +1,6 @@
"""Support for Powerview scenes from a Powerview hub."""
import logging
from typing import Any
from aiopvapi.helpers.aiorequest import AioRequest
from aiopvapi.resources.scene import Scene as PvScene
@ -97,6 +98,6 @@ class PowerViewScene(Scene):
"""Icon to use in the frontend."""
return "mdi:blinds"
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate scene. Try to get entities into requested state."""
await self._scene.activate()

View File

@ -1,7 +1,7 @@
"""Reproduce an input boolean state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -19,7 +19,11 @@ _LOGGER = logging.getLogger(__name__)
async def _async_reproduce_states(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce input boolean states."""
cur_state = hass.states.get(state.entity_id)
@ -49,9 +53,18 @@ async def _async_reproduce_states(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce component states."""
await asyncio.gather(
*(_async_reproduce_states(hass, state, context) for state in states)
*(
_async_reproduce_states(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,7 +1,7 @@
"""Reproduce an Input datetime state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import Context, State
@ -40,7 +40,11 @@ def is_valid_time(string: str) -> bool:
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -97,9 +101,18 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Input datetime states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,7 +1,7 @@
"""Reproduce an Input number state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
import voluptuous as vol
@ -15,7 +15,11 @@ _LOGGER = logging.getLogger(__name__)
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -49,10 +53,19 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Input number states."""
# Reproduce states in parallel.
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -2,7 +2,7 @@
import asyncio
import logging
from types import MappingProxyType
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import Context, State
@ -22,7 +22,11 @@ _LOGGER = logging.getLogger(__name__)
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -64,12 +68,21 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Input select states."""
# Reproduce states in parallel.
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,7 +1,7 @@
"""Reproduce an Input text state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import Context, State
@ -13,7 +13,11 @@ _LOGGER = logging.getLogger(__name__)
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -37,10 +41,19 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Input text states."""
# Reproduce states in parallel.
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,4 +1,6 @@
"""Support for KNX scenes."""
from typing import Any
import voluptuous as vol
from xknx.devices import Scene as XknxScene
@ -65,6 +67,6 @@ class KNXScene(Scene):
"""Return the name of the scene."""
return self.scene.name
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
await self.scene.run()

View File

@ -1,4 +1,6 @@
"""Support for LCN scenes."""
from typing import Any
import pypck
from homeassistant.components.scene import Scene
@ -63,7 +65,7 @@ class LcnScene(LcnDevice, Scene):
async def async_added_to_hass(self):
"""Run when entity about to be added to hass."""
async def async_activate(self):
def activate(self, **kwargs: Any) -> None:
"""Activate scene."""
self.address_connection.activate_scene(
self.register_id,

View File

@ -1,6 +1,7 @@
"""Support for LIFX Cloud scenes."""
import asyncio
import logging
from typing import Any
import aiohttp
from aiohttp.hdrs import AUTHORIZATION
@ -46,9 +47,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
status = scenes_resp.status
if status == HTTP_OK:
data = await scenes_resp.json()
devices = []
for scene in data:
devices.append(LifxCloudScene(hass, headers, timeout, scene))
devices = [LifxCloudScene(hass, headers, timeout, scene) for scene in data]
async_add_entities(devices)
return True
if status == 401:
@ -75,7 +74,7 @@ class LifxCloudScene(Scene):
"""Return the name of the scene."""
return self._name
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
url = f"https://api.lifx.com/v1/scenes/scene_id:{self._uuid}/activate"

View File

@ -40,7 +40,7 @@ SUPPORT_COLOR = 16
SUPPORT_TRANSITION = 32
SUPPORT_WHITE_VALUE = 128
# Integer that represents transition time in seconds to make change.
# Float that represents transition time in seconds to make change.
ATTR_TRANSITION = "transition"
# Lists holding color values

View File

@ -2,7 +2,7 @@
import asyncio
import logging
from types import MappingProxyType
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -71,7 +71,11 @@ DEPRECATION_WARNING = (
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -98,7 +102,10 @@ async def _async_reproduce_state(
):
return
service_data = {ATTR_ENTITY_ID: state.entity_id}
service_data: Dict[str, Any] = {ATTR_ENTITY_ID: state.entity_id}
if reproduce_options is not None and ATTR_TRANSITION in reproduce_options:
service_data[ATTR_TRANSITION] = reproduce_options[ATTR_TRANSITION]
if state.state == STATE_ON:
service = SERVICE_TURN_ON
@ -122,11 +129,20 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Light states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,5 +1,6 @@
"""Support for LiteJet scenes."""
import logging
from typing import Any
from homeassistant.components import litejet
from homeassistant.components.scene import Scene
@ -40,6 +41,6 @@ class LiteJetScene(Scene):
"""Return the device-specific state attributes."""
return {ATTR_NUMBER: self._index}
def activate(self):
def activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
self._lj.activate_scene(self._index)

View File

@ -1,7 +1,7 @@
"""Reproduce an Lock state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -21,7 +21,11 @@ VALID_STATES = {STATE_LOCKED, STATE_UNLOCKED}
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -53,9 +57,18 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Lock states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,5 +1,6 @@
"""Support for Lutron scenes."""
import logging
from typing import Any
from homeassistant.components.scene import Scene
@ -30,7 +31,7 @@ class LutronScene(LutronDevice, Scene):
self._keypad_name = keypad_name
self._led = lutron_led
def activate(self):
def activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
self._lutron_device.press()

View File

@ -1,5 +1,6 @@
"""Support for Lutron Caseta scenes."""
import logging
from typing import Any
from homeassistant.components.scene import Scene
@ -34,6 +35,6 @@ class LutronCasetaScene(Scene):
"""Return the name of the scene."""
return self._scene_name
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
self._bridge.activate_scene(self._scene_id)

View File

@ -1,6 +1,6 @@
"""Module that groups code required to handle state restore for component."""
import asyncio
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
SERVICE_MEDIA_PAUSE,
@ -39,14 +39,17 @@ from .const import (
async def _async_reproduce_states(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce component states."""
async def call_service(service: str, keys: Iterable) -> None:
"""Call service with set of attributes given."""
data = {}
data["entity_id"] = state.entity_id
data = {"entity_id": state.entity_id}
for key in keys:
if key in state.attributes:
data[key] = state.attributes[key]
@ -91,9 +94,18 @@ async def _async_reproduce_states(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce component states."""
await asyncio.gather(
*(_async_reproduce_states(hass, state, context) for state in states)
*(
_async_reproduce_states(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,5 +1,7 @@
"""Support for Nexia Automations."""
from typing import Any
from homeassistant.components.scene import Scene
from homeassistant.helpers.event import async_call_later
@ -48,7 +50,7 @@ class NexiaAutomationScene(NexiaEntity, Scene):
"""Return the icon of the automation scene."""
return "mdi:script-text-outline"
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate an automation scene."""
await self.hass.async_add_executor_job(self._automation.activate)

View File

@ -1,7 +1,7 @@
"""Reproduce an Remote state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -21,7 +21,11 @@ VALID_STATES = {STATE_ON, STATE_OFF}
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -53,9 +57,18 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Remote states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,9 +1,12 @@
"""Allow users to set and activate scenes."""
import functools as ft
import importlib
import logging
from typing import Any, Optional
import voluptuous as vol
from homeassistant.components.light import ATTR_TRANSITION
from homeassistant.const import CONF_PLATFORM, SERVICE_TURN_ON
from homeassistant.core import DOMAIN as HA_DOMAIN
from homeassistant.helpers.entity import Entity
@ -62,7 +65,11 @@ async def async_setup(hass, config):
await component.async_setup(config)
# Ensure Home Assistant platform always loaded.
await component.async_setup_platform(HA_DOMAIN, {"platform": HA_DOMAIN, STATES: []})
component.async_register_entity_service(SERVICE_TURN_ON, {}, "async_activate")
component.async_register_entity_service(
SERVICE_TURN_ON,
{ATTR_TRANSITION: vol.All(vol.Coerce(float), vol.Clamp(min=0, max=6553))},
"async_activate",
)
return True
@ -81,19 +88,22 @@ class Scene(Entity):
"""A scene is a group of entities and the states we want them to be."""
@property
def should_poll(self):
def should_poll(self) -> bool:
"""No polling needed."""
return False
@property
def state(self):
def state(self) -> Optional[str]:
"""Return the state of the scene."""
return STATE
def activate(self):
def activate(self, **kwargs: Any) -> None:
"""Activate scene. Try to get entities into requested state."""
raise NotImplementedError()
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate scene. Try to get entities into requested state."""
await self.hass.async_add_job(self.activate)
assert self.hass
task = self.hass.async_add_job(ft.partial(self.activate, **kwargs))
if task:
await task

View File

@ -3,6 +3,11 @@
turn_on:
description: Activate a scene.
fields:
transition:
description:
Transition duration in seconds it takes to bring devices to the state
defined in the scene.
example: 2.5
entity_id:
description: Name(s) of scenes to turn on
example: "scene.romantic"
@ -11,8 +16,15 @@ reload:
description: Reload the scene configuration
apply:
description: Activate a scene. Takes same data as the entities field from a single scene in the config.
description:
Activate a scene. Takes same data as the entities field from a single scene
in the config.
fields:
transition:
description:
Transition duration in seconds it takes to bring devices to the state
defined in the scene.
example: 2.5
entities:
description: The entities and the state that they need to be.
example:

View File

@ -1,4 +1,6 @@
"""Support for scenes through the SmartThings cloud API."""
from typing import Any
from homeassistant.components.scene import Scene
from .const import DATA_BROKERS, DOMAIN
@ -17,7 +19,7 @@ class SmartThingsScene(Scene):
"""Init the scene class."""
self._scene = scene
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate scene."""
await self._scene.execute()

View File

@ -1,7 +1,7 @@
"""Reproduce an Switch state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -21,7 +21,11 @@ VALID_STATES = {STATE_ON, STATE_OFF}
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -53,9 +57,18 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Switch states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,5 +1,6 @@
"""Support for Tahoma scenes."""
import logging
from typing import Any
from homeassistant.components.scene import Scene
@ -13,9 +14,10 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
if discovery_info is None:
return
controller = hass.data[TAHOMA_DOMAIN]["controller"]
scenes = []
for scene in hass.data[TAHOMA_DOMAIN]["scenes"]:
scenes.append(TahomaScene(scene, controller))
scenes = [
TahomaScene(scene, controller) for scene in hass.data[TAHOMA_DOMAIN]["scenes"]
]
add_entities(scenes, True)
@ -28,7 +30,7 @@ class TahomaScene(Scene):
self.controller = controller
self._name = self.tahoma_scene.name
def activate(self):
def activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
self.controller.launch_action_group(self.tahoma_scene.oid)

View File

@ -1,7 +1,7 @@
"""Reproduce an Timer state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import Context, State
@ -24,7 +24,11 @@ VALID_STATES = {STATUS_IDLE, STATUS_ACTIVE, STATUS_PAUSED}
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -62,9 +66,18 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Timer states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,4 +1,6 @@
"""Support for the Tuya scenes."""
from typing import Any
from homeassistant.components.scene import DOMAIN, Scene
from . import DATA_TUYA, TuyaDevice
@ -29,6 +31,6 @@ class TuyaScene(TuyaDevice, Scene):
super().__init__(tuya)
self.entity_id = ENTITY_ID_FORMAT.format(tuya.object_id())
def activate(self):
def activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
self.tuya.activate()

View File

@ -1,7 +1,7 @@
"""Reproduce an Vacuum state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -41,7 +41,11 @@ VALID_STATES_STATE = {
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -50,7 +54,7 @@ async def _async_reproduce_state(
_LOGGER.warning("Unable to find entity %s", state.entity_id)
return
if state.state not in VALID_STATES_TOGGLE and state.state not in VALID_STATES_STATE:
if not (state.state in VALID_STATES_TOGGLE or state.state in VALID_STATES_STATE):
_LOGGER.warning(
"Invalid state specified for %s: %s", state.entity_id, state.state
)
@ -72,7 +76,7 @@ async def _async_reproduce_state(
service = SERVICE_TURN_OFF
elif state.state == STATE_CLEANING:
service = SERVICE_START
elif state.state == STATE_DOCKED or state.state == STATE_RETURNING:
elif state.state in [STATE_DOCKED, STATE_RETURNING]:
service = SERVICE_RETURN_TO_BASE
elif state.state == STATE_IDLE:
service = SERVICE_STOP
@ -92,10 +96,19 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Vacuum states."""
# Reproduce states in parallel.
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,4 +1,6 @@
"""Support for VELUX scenes."""
from typing import Any
from homeassistant.components.scene import Scene
from . import _LOGGER, DATA_VELUX
@ -6,9 +8,7 @@ from . import _LOGGER, DATA_VELUX
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the scenes for Velux platform."""
entities = []
for scene in hass.data[DATA_VELUX].pyvlx.scenes:
entities.append(VeluxScene(scene))
entities = [VeluxScene(scene) for scene in hass.data[DATA_VELUX].pyvlx.scenes]
async_add_entities(entities)
@ -25,6 +25,6 @@ class VeluxScene(Scene):
"""Return the name of the scene."""
return self.scene.name
async def async_activate(self):
async def async_activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
await self.scene.run(wait_for_completion=False)

View File

@ -1,6 +1,6 @@
"""Support for Vera scenes."""
import logging
from typing import Callable, List
from typing import Any, Callable, List
from homeassistant.components.scene import Scene
from homeassistant.config_entries import ConfigEntry
@ -46,7 +46,7 @@ class VeraScene(Scene):
"""Update the scene status."""
self.vera_scene.refresh()
def activate(self):
def activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
self.vera_scene.activate()

View File

@ -1,7 +1,7 @@
"""Reproduce an Water heater state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -44,7 +44,11 @@ VALID_STATES = {
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -117,9 +121,18 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce Water heater states."""
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)

View File

@ -1,5 +1,6 @@
"""Support for Wink scenes."""
import logging
from typing import Any
import pywink
@ -31,6 +32,6 @@ class WinkScene(WinkDevice, Scene):
"""Call when entity is added to hass."""
self.hass.data[DOMAIN]["entities"]["scene"].append(self)
def activate(self):
def activate(self, **kwargs: Any) -> None:
"""Activate the scene."""
self.wink.activate()

View File

@ -4,7 +4,7 @@ from collections import defaultdict
import datetime as dt
import logging
from types import ModuleType, TracebackType
from typing import Dict, Iterable, List, Optional, Type, Union
from typing import Any, Dict, Iterable, List, Optional, Type, Union
from homeassistant.components.sun import STATE_ABOVE_HORIZON, STATE_BELOW_HORIZON
from homeassistant.const import (
@ -69,8 +69,9 @@ def get_changed_since(
async def async_reproduce_state(
hass: HomeAssistantType,
states: Union[State, Iterable[State]],
blocking: bool = False,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a list of states on multiple domains."""
if isinstance(states, State):
@ -97,7 +98,7 @@ async def async_reproduce_state(
return
await platform.async_reproduce_states( # type: ignore
hass, states_by_domain, context=context
hass, states_by_domain, context=context, reproduce_options=reproduce_options
)
if to_call:

View File

@ -1,7 +1,7 @@
"""Reproduce an NEW_NAME state."""
import asyncio
import logging
from typing import Iterable, Optional
from typing import Any, Dict, Iterable, Optional
from homeassistant.const import (
ATTR_ENTITY_ID,
@ -22,7 +22,11 @@ VALID_STATES = {STATE_ON, STATE_OFF}
async def _async_reproduce_state(
hass: HomeAssistantType, state: State, context: Optional[Context] = None
hass: HomeAssistantType,
state: State,
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce a single state."""
cur_state = hass.states.get(state.entity_id)
@ -63,16 +67,25 @@ async def _async_reproduce_state(
async def async_reproduce_states(
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
hass: HomeAssistantType,
states: Iterable[State],
*,
context: Optional[Context] = None,
reproduce_options: Optional[Dict[str, Any]] = None,
) -> None:
"""Reproduce NEW_NAME states."""
# TODO pick one and remove other one
# Reproduce states in parallel.
await asyncio.gather(
*(_async_reproduce_state(hass, state, context) for state in states)
*(
_async_reproduce_state(
hass, state, context=context, reproduce_options=reproduce_options
)
for state in states
)
)
# Alternative: Reproduce states in sequence
# for state in states:
# await _async_reproduce_state(hass, state, context)
# await _async_reproduce_state(hass, state, context=context, reproduce_options=reproduce_options)

View File

@ -70,8 +70,7 @@ async def test_reproducing_states(hass, caplog):
State("alarm_control_panel.entity_armed_night", STATE_ALARM_ARMED_NIGHT),
State("alarm_control_panel.entity_disarmed", STATE_ALARM_DISARMED),
State("alarm_control_panel.entity_triggered", STATE_ALARM_TRIGGERED),
],
blocking=True,
]
)
assert len(arm_away_calls) == 0
@ -83,7 +82,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("alarm_control_panel.entity_triggered", "not_supported")], blocking=True
[State("alarm_control_panel.entity_triggered", "not_supported")]
)
assert "not_supported" in caplog.text
@ -109,8 +108,7 @@ async def test_reproducing_states(hass, caplog):
State("alarm_control_panel.entity_triggered", STATE_ALARM_DISARMED),
# Should not raise
State("alarm_control_panel.non_existing", "on"),
],
blocking=True,
]
)
assert len(arm_away_calls) == 1

View File

@ -14,8 +14,7 @@ async def test_reproducing_states(hass, caplog):
# These calls should do nothing as entities already in desired state
await hass.helpers.state.async_reproduce_state(
[State("automation.entity_off", "off"), State("automation.entity_on", "on")],
blocking=True,
[State("automation.entity_off", "off"), State("automation.entity_on", "on")]
)
assert len(turn_on_calls) == 0
@ -23,7 +22,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("automation.entity_off", "not_supported")], blocking=True
[State("automation.entity_off", "not_supported")]
)
assert "not_supported" in caplog.text
@ -37,8 +36,7 @@ async def test_reproducing_states(hass, caplog):
State("automation.entity_off", "on"),
# Should not raise
State("automation.non_existing", "on"),
],
blocking=True,
]
)
assert len(turn_on_calls) == 1

View File

@ -82,7 +82,9 @@ async def test_state_with_context(hass):
context = Context()
await async_reproduce_states(hass, [State(ENTITY_1, HVAC_MODE_HEAT)], context)
await async_reproduce_states(
hass, [State(ENTITY_1, HVAC_MODE_HEAT)], context=context
)
await hass.async_block_till_done()

View File

@ -24,15 +24,14 @@ async def test_reproducing_states(hass, caplog):
"8",
{"initial": 12, "minimum": 5, "maximum": 15, "step": 3},
),
],
blocking=True,
]
)
assert len(configure_calls) == 0
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("counter.entity", "not_supported")], blocking=True
[State("counter.entity", "not_supported")]
)
assert "not_supported" in caplog.text
@ -49,8 +48,7 @@ async def test_reproducing_states(hass, caplog):
),
# Should not raise
State("counter.non_existing", "6"),
],
blocking=True,
]
)
valid_calls = [

View File

@ -93,8 +93,7 @@ async def test_reproducing_states(hass, caplog):
STATE_OPEN,
{ATTR_CURRENT_POSITION: 100, ATTR_CURRENT_TILT_POSITION: 100},
),
],
blocking=True,
]
)
assert len(close_calls) == 0
@ -106,7 +105,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("cover.entity_close", "not_supported")], blocking=True
[State("cover.entity_close", "not_supported")]
)
assert "not_supported" in caplog.text
@ -145,7 +144,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("cover.non_existing", "on"),
],
blocking=True,
)
valid_close_calls = [

View File

@ -27,7 +27,6 @@ async def test_reproducing_states(hass, caplog):
State("fan.entity_oscillating", "on", {"oscillating": True}),
State("fan.entity_direction", "on", {"direction": "forward"}),
],
blocking=True,
)
assert len(turn_on_calls) == 0
@ -38,7 +37,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("fan.entity_off", "not_supported")], blocking=True
[State("fan.entity_off", "not_supported")]
)
assert "not_supported" in caplog.text
@ -59,7 +58,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("fan.non_existing", "on"),
],
blocking=True,
)
assert len(turn_on_calls) == 1

View File

@ -39,7 +39,7 @@ async def test_reproduce_group(hass):
state = State("group.test", "on")
await async_reproduce_states(hass, [state], context)
await async_reproduce_states(hass, [state], context=context)
fun.assert_called_once_with(
hass,
@ -48,6 +48,6 @@ async def test_reproduce_group(hass):
clone_state(state, "light.test2"),
clone_state(state, "switch.test1"),
],
blocking=True,
context=context,
reproduce_options=None,
)

View File

@ -58,6 +58,24 @@ async def test_apply_service(hass):
assert state.state == "on"
assert state.attributes["brightness"] == 50
turn_on_calls = async_mock_service(hass, "light", "turn_on")
assert await hass.services.async_call(
"scene",
"apply",
{
"transition": 42,
"entities": {"light.bed_light": {"state": "on", "brightness": 50}},
},
blocking=True,
)
assert len(turn_on_calls) == 1
assert turn_on_calls[0].domain == "light"
assert turn_on_calls[0].service == "turn_on"
assert turn_on_calls[0].data.get("transition") == 42
assert turn_on_calls[0].data.get("entity_id") == "light.bed_light"
assert turn_on_calls[0].data.get("brightness") == 50
async def test_create_service(hass, caplog):
"""Test the create service."""

View File

@ -22,7 +22,6 @@ async def test_reproducing_states(hass):
# Should not raise
State("input_boolean.non_existing", "on"),
],
blocking=True,
)
assert hass.states.get("input_boolean.initial_off").state == "on"
assert hass.states.get("input_boolean.initial_on").state == "off"
@ -34,7 +33,6 @@ async def test_reproducing_states(hass):
# Set to state it already is.
State("input_boolean.initial_off", "on"),
],
blocking=True,
)
assert hass.states.get("input_boolean.initial_on").state == "off"

View File

@ -29,7 +29,6 @@ async def test_reproducing_states(hass, caplog):
State("input_datetime.entity_time", "01:20:00"),
State("input_datetime.entity_date", "2010-10-10"),
],
blocking=True,
)
assert len(datetime_calls) == 0
@ -42,7 +41,6 @@ async def test_reproducing_states(hass, caplog):
State("input_datetime.entity_datetime", "not:valid:time"),
State("input_datetime.entity_datetime", "1234-56-78 90:12:34"),
],
blocking=True,
)
assert "not_supported" in caplog.text
@ -60,7 +58,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("input_datetime.non_existing", "2010-10-10 01:20:00"),
],
blocking=True,
)
valid_calls = [

View File

@ -26,7 +26,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("input_number.non_existing", "234"),
],
blocking=True,
)
assert hass.states.get("input_number.test_number").state == VALID_NUMBER1
@ -38,14 +37,13 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("input_number.non_existing", "234"),
],
blocking=True,
)
assert hass.states.get("input_number.test_number").state == VALID_NUMBER2
# Test setting state to number out of range
await hass.helpers.state.async_reproduce_state(
[State("input_number.test_number", "150")], blocking=True
[State("input_number.test_number", "150")]
)
# The entity states should be unchanged after trying to set them to out-of-range number
@ -58,5 +56,4 @@ async def test_reproducing_states(hass, caplog):
# Set to state it already is.
State("input_number.test_number", VALID_NUMBER2),
],
blocking=True,
)

View File

@ -35,7 +35,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("input_select.non_existing", VALID_OPTION1),
],
blocking=True,
)
# Test that entity is in desired state
@ -48,23 +47,20 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("input_select.non_existing", VALID_OPTION3),
],
blocking=True,
)
# Test that we got the desired result
assert hass.states.get(ENTITY).state == VALID_OPTION3
# Test setting state to invalid state
await hass.helpers.state.async_reproduce_state(
[State(ENTITY, INVALID_OPTION)], blocking=True
)
await hass.helpers.state.async_reproduce_state([State(ENTITY, INVALID_OPTION)])
# The entity state should be unchanged
assert hass.states.get(ENTITY).state == VALID_OPTION3
# Test setting a different option set
await hass.helpers.state.async_reproduce_state(
[State(ENTITY, VALID_OPTION5, {"options": VALID_OPTION_SET2})], blocking=True
[State(ENTITY, VALID_OPTION5, {"options": VALID_OPTION_SET2})]
)
# These should fail if options weren't changed to VALID_OPTION_SET2

View File

@ -29,7 +29,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("input_text.non_existing", VALID_TEXT1),
],
blocking=True,
)
# Test that entity is in desired state
@ -42,7 +41,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("input_text.non_existing", VALID_TEXT2),
],
blocking=True,
)
# Test that the state was changed
@ -50,7 +48,7 @@ async def test_reproducing_states(hass, caplog):
# Test setting state to invalid state (length too long)
await hass.helpers.state.async_reproduce_state(
[State("input_text.test_text", INVALID_TEXT1)], blocking=True
[State("input_text.test_text", INVALID_TEXT1)]
)
# The entity state should be unchanged
@ -58,7 +56,7 @@ async def test_reproducing_states(hass, caplog):
# Test setting state to invalid state (length too short)
await hass.helpers.state.async_reproduce_state(
[State("input_text.test_text", INVALID_TEXT2)], blocking=True
[State("input_text.test_text", INVALID_TEXT2)]
)
# The entity state should be unchanged

View File

@ -53,8 +53,7 @@ async def test_reproducing_states(hass, caplog):
State("light.entity_profile", "on", VALID_PROFILE),
State("light.entity_rgb", "on", VALID_RGB_COLOR),
State("light.entity_xy", "on", VALID_XY_COLOR),
],
blocking=True,
]
)
assert len(turn_on_calls) == 0
@ -62,7 +61,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("light.entity_off", "not_supported")], blocking=True
[State("light.entity_off", "not_supported")]
)
assert "not_supported" in caplog.text
@ -86,7 +85,6 @@ async def test_reproducing_states(hass, caplog):
State("light.entity_profile", "on", VALID_RGB_COLOR),
State("light.entity_rgb", "on", VALID_XY_COLOR),
],
blocking=True,
)
assert len(turn_on_calls) == 12
@ -163,7 +161,7 @@ async def test_deprecation_warning(hass, caplog):
hass.states.async_set("light.entity_off", "off", {})
turn_on_calls = async_mock_service(hass, "light", "turn_on")
await hass.helpers.state.async_reproduce_state(
[State("light.entity_off", "on", {"brightness_pct": 80})], blocking=True
[State("light.entity_off", "on", {"brightness_pct": 80})]
)
assert len(turn_on_calls) == 1
assert DEPRECATION_WARNING % ["brightness_pct"] in caplog.text

View File

@ -18,7 +18,6 @@ async def test_reproducing_states(hass, caplog):
State("lock.entity_locked", "locked"),
State("lock.entity_unlocked", "unlocked", {}),
],
blocking=True,
)
assert len(lock_calls) == 0
@ -26,7 +25,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("lock.entity_locked", "not_supported")], blocking=True
[State("lock.entity_locked", "not_supported")]
)
assert "not_supported" in caplog.text
@ -41,7 +40,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("lock.non_existing", "on"),
],
blocking=True,
)
assert len(lock_calls) == 1

View File

@ -115,7 +115,7 @@ async def test_state_with_context(hass):
context = Context()
await async_reproduce_states(hass, [State(ENTITY_1, "on")], context)
await async_reproduce_states(hass, [State(ENTITY_1, "on")], context=context)
await hass.async_block_till_done()

View File

@ -15,7 +15,6 @@ async def test_reproducing_states(hass, caplog):
# These calls should do nothing as entities already in desired state
await hass.helpers.state.async_reproduce_state(
[State("remote.entity_off", "off"), State("remote.entity_on", "on")],
blocking=True,
)
assert len(turn_on_calls) == 0
@ -23,7 +22,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("remote.entity_off", "not_supported")], blocking=True
[State("remote.entity_off", "not_supported")]
)
assert "not_supported" in caplog.text
@ -38,7 +37,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("remote.non_existing", "on"),
],
blocking=True,
)
assert len(turn_on_calls) == 1

View File

@ -6,7 +6,7 @@ from homeassistant.components import light, scene
from homeassistant.setup import async_setup_component, setup_component
from homeassistant.util.yaml import loader as yaml_loader
from tests.common import get_test_home_assistant
from tests.common import get_test_home_assistant, mock_service
from tests.components.light import common as common_light
from tests.components.scene import common
@ -127,7 +127,19 @@ class TestScene(unittest.TestCase):
assert self.light_1.is_on
assert self.light_2.is_on
assert 100 == self.light_2.last_call("turn_on")[1].get("brightness")
assert self.light_2.last_call("turn_on")[1].get("brightness") == 100
turn_on_calls = mock_service(self.hass, "light", "turn_on")
self.hass.services.call(
scene.DOMAIN, "turn_on", {"transition": 42, "entity_id": "scene.test"}
)
self.hass.block_till_done()
assert len(turn_on_calls) == 1
assert turn_on_calls[0].domain == "light"
assert turn_on_calls[0].service == "turn_on"
assert turn_on_calls[0].data.get("transition") == 42
async def test_services_registered(hass):

View File

@ -15,7 +15,6 @@ async def test_reproducing_states(hass, caplog):
# These calls should do nothing as entities already in desired state
await hass.helpers.state.async_reproduce_state(
[State("switch.entity_off", "off"), State("switch.entity_on", "on", {})],
blocking=True,
)
assert len(turn_on_calls) == 0
@ -23,7 +22,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("switch.entity_off", "not_supported")], blocking=True
[State("switch.entity_off", "not_supported")]
)
assert "not_supported" in caplog.text
@ -37,8 +36,7 @@ async def test_reproducing_states(hass, caplog):
State("switch.entity_off", "on", {}),
# Should not raise
State("switch.non_existing", "on"),
],
blocking=True,
]
)
assert len(turn_on_calls) == 1

View File

@ -36,7 +36,6 @@ async def test_reproducing_states(hass, caplog):
"timer.entity_active_attr", STATUS_ACTIVE, {ATTR_DURATION: "00:01:00"}
),
],
blocking=True,
)
assert len(start_calls) == 0
@ -45,7 +44,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("timer.entity_idle", "not_supported")], blocking=True
[State("timer.entity_idle", "not_supported")]
)
assert "not_supported" in caplog.text
@ -63,7 +62,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("timer.non_existing", "on"),
],
blocking=True,
)
valid_start_calls = [

View File

@ -59,7 +59,6 @@ async def test_reproducing_states(hass, caplog):
State("vacuum.entity_returning", STATE_RETURNING),
State("vacuum.entity_paused", STATE_PAUSED),
],
blocking=True,
)
assert len(turn_on_calls) == 0
@ -72,7 +71,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("vacuum.entity_off", "not_supported")], blocking=True
[State("vacuum.entity_off", "not_supported")]
)
assert "not_supported" in caplog.text
@ -98,7 +97,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("vacuum.non_existing", STATE_ON),
],
blocking=True,
)
assert len(turn_on_calls) == 1

View File

@ -45,8 +45,7 @@ async def test_reproducing_states(hass, caplog):
STATE_ECO,
{ATTR_AWAY_MODE: True, ATTR_TEMPERATURE: 45},
),
],
blocking=True,
]
)
assert len(turn_on_calls) == 0
@ -57,7 +56,7 @@ async def test_reproducing_states(hass, caplog):
# Test invalid state is handled
await hass.helpers.state.async_reproduce_state(
[State("water_heater.entity_off", "not_supported")], blocking=True
[State("water_heater.entity_off", "not_supported")]
)
assert "not_supported" in caplog.text
@ -82,7 +81,6 @@ async def test_reproducing_states(hass, caplog):
# Should not raise
State("water_heater.non_existing", "on"),
],
blocking=True,
)
assert len(turn_on_calls) == 1

View File

@ -68,17 +68,16 @@ async def test_call_to_component(hass):
context = "dummy_context"
await state.async_reproduce_state(
hass,
[state_media_player, state_climate],
blocking=True,
context=context,
hass, [state_media_player, state_climate], context=context,
)
media_player_fun.assert_called_once_with(
hass, [state_media_player], context=context
hass, [state_media_player], context=context, reproduce_options=None
)
climate_fun.assert_called_once_with(hass, [state_climate], context=context)
climate_fun.assert_called_once_with(
hass, [state_climate], context=context, reproduce_options=None
)
async def test_get_changed_since(hass):