Merge pull request #47490 from home-assistant/rc

This commit is contained in:
Paulus Schoutsen 2021-03-05 16:22:59 -08:00 committed by GitHub
commit 1145c30c4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 134 additions and 128 deletions

View File

@ -2,7 +2,7 @@
"domain": "amcrest", "domain": "amcrest",
"name": "Amcrest", "name": "Amcrest",
"documentation": "https://www.home-assistant.io/integrations/amcrest", "documentation": "https://www.home-assistant.io/integrations/amcrest",
"requirements": ["amcrest==1.7.0"], "requirements": ["amcrest==1.7.1"],
"dependencies": ["ffmpeg"], "dependencies": ["ffmpeg"],
"codeowners": ["@pnbruckner"] "codeowners": ["@pnbruckner"]
} }

View File

@ -3,7 +3,7 @@
"name": "Home Assistant Frontend", "name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/integrations/frontend", "documentation": "https://www.home-assistant.io/integrations/frontend",
"requirements": [ "requirements": [
"home-assistant-frontend==20210302.4" "home-assistant-frontend==20210302.5"
], ],
"dependencies": [ "dependencies": [
"api", "api",

View File

@ -19,7 +19,6 @@ from .const import (
CONF_ALLOW_UNREACHABLE, CONF_ALLOW_UNREACHABLE,
DEFAULT_ALLOW_HUE_GROUPS, DEFAULT_ALLOW_HUE_GROUPS,
DEFAULT_ALLOW_UNREACHABLE, DEFAULT_ALLOW_UNREACHABLE,
DEFAULT_SCENE_TRANSITION,
LOGGER, LOGGER,
) )
from .errors import AuthenticationRequired, CannotConnect from .errors import AuthenticationRequired, CannotConnect
@ -34,9 +33,7 @@ SCENE_SCHEMA = vol.Schema(
{ {
vol.Required(ATTR_GROUP_NAME): cv.string, vol.Required(ATTR_GROUP_NAME): cv.string,
vol.Required(ATTR_SCENE_NAME): cv.string, vol.Required(ATTR_SCENE_NAME): cv.string,
vol.Optional( vol.Optional(ATTR_TRANSITION): cv.positive_int,
ATTR_TRANSITION, default=DEFAULT_SCENE_TRANSITION
): cv.positive_int,
} }
) )
# How long should we sleep if the hub is busy # How long should we sleep if the hub is busy
@ -209,7 +206,7 @@ class HueBridge:
"""Service to call directly into bridge to set scenes.""" """Service to call directly into bridge to set scenes."""
group_name = call.data[ATTR_GROUP_NAME] group_name = call.data[ATTR_GROUP_NAME]
scene_name = call.data[ATTR_SCENE_NAME] scene_name = call.data[ATTR_SCENE_NAME]
transition = call.data.get(ATTR_TRANSITION, DEFAULT_SCENE_TRANSITION) transition = call.data.get(ATTR_TRANSITION)
group = next( group = next(
(group for group in self.api.groups.values() if group.name == group_name), (group for group in self.api.groups.values() if group.name == group_name),

View File

@ -14,8 +14,6 @@ DEFAULT_ALLOW_UNREACHABLE = False
CONF_ALLOW_HUE_GROUPS = "allow_hue_groups" CONF_ALLOW_HUE_GROUPS = "allow_hue_groups"
DEFAULT_ALLOW_HUE_GROUPS = False DEFAULT_ALLOW_HUE_GROUPS = False
DEFAULT_SCENE_TRANSITION = 4
GROUP_TYPE_LIGHT_GROUP = "LightGroup" GROUP_TYPE_LIGHT_GROUP = "LightGroup"
GROUP_TYPE_ROOM = "Room" GROUP_TYPE_ROOM = "Room"
GROUP_TYPE_LUMINAIRE = "Luminaire" GROUP_TYPE_LUMINAIRE = "Luminaire"

View File

@ -29,7 +29,6 @@ CONF_GATEWAY_TYPE_ALL: List[str] = [
DOMAIN: str = "mysensors" DOMAIN: str = "mysensors"
MYSENSORS_GATEWAY_READY: str = "mysensors_gateway_ready_{}"
MYSENSORS_GATEWAY_START_TASK: str = "mysensors_gateway_start_task_{}" MYSENSORS_GATEWAY_START_TASK: str = "mysensors_gateway_start_task_{}"
MYSENSORS_GATEWAYS: str = "mysensors_gateways" MYSENSORS_GATEWAYS: str = "mysensors_gateways"
PLATFORM: str = "platform" PLATFORM: str = "platform"

View File

@ -26,7 +26,6 @@ from .const import (
CONF_TOPIC_OUT_PREFIX, CONF_TOPIC_OUT_PREFIX,
CONF_VERSION, CONF_VERSION,
DOMAIN, DOMAIN,
MYSENSORS_GATEWAY_READY,
MYSENSORS_GATEWAY_START_TASK, MYSENSORS_GATEWAY_START_TASK,
MYSENSORS_GATEWAYS, MYSENSORS_GATEWAYS,
GatewayId, GatewayId,
@ -36,7 +35,7 @@ from .helpers import discover_mysensors_platform, validate_child, validate_node
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
GATEWAY_READY_TIMEOUT = 15.0 GATEWAY_READY_TIMEOUT = 20.0
MQTT_COMPONENT = "mqtt" MQTT_COMPONENT = "mqtt"
@ -64,24 +63,16 @@ async def try_connect(hass: HomeAssistantType, user_input: Dict[str, str]) -> bo
if user_input[CONF_DEVICE] == MQTT_COMPONENT: if user_input[CONF_DEVICE] == MQTT_COMPONENT:
return True # dont validate mqtt. mqtt gateways dont send ready messages :( return True # dont validate mqtt. mqtt gateways dont send ready messages :(
try: try:
gateway_ready = asyncio.Future() gateway_ready = asyncio.Event()
def gateway_ready_callback(msg): def on_conn_made(_: BaseAsyncGateway) -> None:
msg_type = msg.gateway.const.MessageType(msg.type) gateway_ready.set()
_LOGGER.debug("Received MySensors msg type %s: %s", msg_type.name, msg)
if msg_type.name != "internal":
return
internal = msg.gateway.const.Internal(msg.sub_type)
if internal.name != "I_GATEWAY_READY":
return
_LOGGER.debug("Received gateway ready")
gateway_ready.set_result(True)
gateway: Optional[BaseAsyncGateway] = await _get_gateway( gateway: Optional[BaseAsyncGateway] = await _get_gateway(
hass, hass,
device=user_input[CONF_DEVICE], device=user_input[CONF_DEVICE],
version=user_input[CONF_VERSION], version=user_input[CONF_VERSION],
event_callback=gateway_ready_callback, event_callback=lambda _: None,
persistence_file=None, persistence_file=None,
baud_rate=user_input.get(CONF_BAUD_RATE), baud_rate=user_input.get(CONF_BAUD_RATE),
tcp_port=user_input.get(CONF_TCP_PORT), tcp_port=user_input.get(CONF_TCP_PORT),
@ -92,12 +83,13 @@ async def try_connect(hass: HomeAssistantType, user_input: Dict[str, str]) -> bo
) )
if gateway is None: if gateway is None:
return False return False
gateway.on_conn_made = on_conn_made
connect_task = None connect_task = None
try: try:
connect_task = asyncio.create_task(gateway.start()) connect_task = asyncio.create_task(gateway.start())
with async_timeout.timeout(20): with async_timeout.timeout(GATEWAY_READY_TIMEOUT):
await gateway_ready await gateway_ready.wait()
return True return True
except asyncio.TimeoutError: except asyncio.TimeoutError:
_LOGGER.info("Try gateway connect failed with timeout") _LOGGER.info("Try gateway connect failed with timeout")
@ -280,6 +272,12 @@ async def _gw_start(
hass: HomeAssistantType, entry: ConfigEntry, gateway: BaseAsyncGateway hass: HomeAssistantType, entry: ConfigEntry, gateway: BaseAsyncGateway
): ):
"""Start the gateway.""" """Start the gateway."""
gateway_ready = asyncio.Event()
def gateway_connected(_: BaseAsyncGateway):
gateway_ready.set()
gateway.on_conn_made = gateway_connected
# Don't use hass.async_create_task to avoid holding up setup indefinitely. # Don't use hass.async_create_task to avoid holding up setup indefinitely.
hass.data[DOMAIN][ hass.data[DOMAIN][
MYSENSORS_GATEWAY_START_TASK.format(entry.entry_id) MYSENSORS_GATEWAY_START_TASK.format(entry.entry_id)
@ -294,21 +292,15 @@ async def _gw_start(
if entry.data[CONF_DEVICE] == MQTT_COMPONENT: if entry.data[CONF_DEVICE] == MQTT_COMPONENT:
# Gatways connected via mqtt doesn't send gateway ready message. # Gatways connected via mqtt doesn't send gateway ready message.
return return
gateway_ready = asyncio.Future()
gateway_ready_key = MYSENSORS_GATEWAY_READY.format(entry.entry_id)
hass.data[DOMAIN][gateway_ready_key] = gateway_ready
try: try:
with async_timeout.timeout(GATEWAY_READY_TIMEOUT): with async_timeout.timeout(GATEWAY_READY_TIMEOUT):
await gateway_ready await gateway_ready.wait()
except asyncio.TimeoutError: except asyncio.TimeoutError:
_LOGGER.warning( _LOGGER.warning(
"Gateway %s not ready after %s secs so continuing with setup", "Gateway %s not connected after %s secs so continuing with setup",
entry.data[CONF_DEVICE], entry.data[CONF_DEVICE],
GATEWAY_READY_TIMEOUT, GATEWAY_READY_TIMEOUT,
) )
finally:
hass.data[DOMAIN].pop(gateway_ready_key, None)
def _gw_callback_factory( def _gw_callback_factory(

View File

@ -8,14 +8,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util import decorator from homeassistant.util import decorator
from .const import ( from .const import CHILD_CALLBACK, NODE_CALLBACK, DevId, GatewayId
CHILD_CALLBACK,
DOMAIN,
MYSENSORS_GATEWAY_READY,
NODE_CALLBACK,
DevId,
GatewayId,
)
from .device import get_mysensors_devices from .device import get_mysensors_devices
from .helpers import discover_mysensors_platform, validate_set_msg from .helpers import discover_mysensors_platform, validate_set_msg
@ -75,20 +68,6 @@ async def handle_sketch_version(
_handle_node_update(hass, gateway_id, msg) _handle_node_update(hass, gateway_id, msg)
@HANDLERS.register("I_GATEWAY_READY")
async def handle_gateway_ready(
hass: HomeAssistantType, gateway_id: GatewayId, msg: Message
) -> None:
"""Handle an internal gateway ready message.
Set asyncio future result if gateway is ready.
"""
gateway_ready = hass.data[DOMAIN].get(MYSENSORS_GATEWAY_READY.format(gateway_id))
if gateway_ready is None or gateway_ready.cancelled():
return
gateway_ready.set_result(True)
@callback @callback
def _handle_child_update( def _handle_child_update(
hass: HomeAssistantType, gateway_id: GatewayId, validated: Dict[str, List[DevId]] hass: HomeAssistantType, gateway_id: GatewayId, validated: Dict[str, List[DevId]]

View File

@ -7,6 +7,7 @@ import voluptuous as vol
from homeassistant.components.camera import SUPPORT_STREAM, Camera from homeassistant.components.camera import SUPPORT_STREAM, Camera
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -49,15 +50,17 @@ async def async_setup_entry(hass, entry, async_add_entities):
data_handler = hass.data[DOMAIN][entry.entry_id][DATA_HANDLER] data_handler = hass.data[DOMAIN][entry.entry_id][DATA_HANDLER]
async def get_entities():
"""Retrieve Netatmo entities."""
await data_handler.register_data_class( await data_handler.register_data_class(
CAMERA_DATA_CLASS_NAME, CAMERA_DATA_CLASS_NAME, None CAMERA_DATA_CLASS_NAME, CAMERA_DATA_CLASS_NAME, None
) )
data = data_handler.data if CAMERA_DATA_CLASS_NAME not in data_handler.data:
raise PlatformNotReady
if not data.get(CAMERA_DATA_CLASS_NAME): async def get_entities():
"""Retrieve Netatmo entities."""
if not data_handler.data.get(CAMERA_DATA_CLASS_NAME):
return [] return []
data_class = data_handler.data[CAMERA_DATA_CLASS_NAME] data_class = data_handler.data[CAMERA_DATA_CLASS_NAME]
@ -94,9 +97,10 @@ async def async_setup_entry(hass, entry, async_add_entities):
async_add_entities(await get_entities(), True) async_add_entities(await get_entities(), True)
await data_handler.unregister_data_class(CAMERA_DATA_CLASS_NAME, None)
platform = entity_platform.current_platform.get() platform = entity_platform.current_platform.get()
if data_handler.data[CAMERA_DATA_CLASS_NAME] is not None:
platform.async_register_entity_service( platform.async_register_entity_service(
SERVICE_SET_PERSONS_HOME, SERVICE_SET_PERSONS_HOME,
{vol.Required(ATTR_PERSONS): vol.All(cv.ensure_list, [cv.string])}, {vol.Required(ATTR_PERSONS): vol.All(cv.ensure_list, [cv.string])},

View File

@ -25,6 +25,7 @@ from homeassistant.const import (
TEMP_CELSIUS, TEMP_CELSIUS,
) )
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.device_registry import async_get_registry from homeassistant.helpers.device_registry import async_get_registry
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -81,6 +82,7 @@ NETATMO_MAP_PRESET = {
STATE_NETATMO_AWAY: PRESET_AWAY, STATE_NETATMO_AWAY: PRESET_AWAY,
STATE_NETATMO_OFF: STATE_NETATMO_OFF, STATE_NETATMO_OFF: STATE_NETATMO_OFF,
STATE_NETATMO_MANUAL: STATE_NETATMO_MANUAL, STATE_NETATMO_MANUAL: STATE_NETATMO_MANUAL,
STATE_NETATMO_HOME: PRESET_SCHEDULE,
} }
HVAC_MAP_NETATMO = { HVAC_MAP_NETATMO = {
@ -111,8 +113,8 @@ async def async_setup_entry(hass, entry, async_add_entities):
) )
home_data = data_handler.data.get(HOMEDATA_DATA_CLASS_NAME) home_data = data_handler.data.get(HOMEDATA_DATA_CLASS_NAME)
if not home_data: if HOMEDATA_DATA_CLASS_NAME not in data_handler.data:
return raise PlatformNotReady
async def get_entities(): async def get_entities():
"""Retrieve Netatmo entities.""" """Retrieve Netatmo entities."""
@ -151,6 +153,8 @@ async def async_setup_entry(hass, entry, async_add_entities):
async_add_entities(await get_entities(), True) async_add_entities(await get_entities(), True)
await data_handler.unregister_data_class(HOMEDATA_DATA_CLASS_NAME, None)
platform = entity_platform.current_platform.get() platform = entity_platform.current_platform.get()
if home_data is not None: if home_data is not None:

View File

@ -129,7 +129,11 @@ class NetatmoDataHandler:
if update_callback: if update_callback:
update_callback() update_callback()
except (pyatmo.NoDevice, pyatmo.ApiError) as err: except pyatmo.NoDevice as err:
_LOGGER.debug(err)
self.data[data_class_entry] = None
except pyatmo.ApiError as err:
_LOGGER.debug(err) _LOGGER.debug(err)
async def register_data_class( async def register_data_class(

View File

@ -31,18 +31,15 @@ async def async_setup_entry(hass, entry, async_add_entities):
data_handler = hass.data[DOMAIN][entry.entry_id][DATA_HANDLER] data_handler = hass.data[DOMAIN][entry.entry_id][DATA_HANDLER]
if CAMERA_DATA_CLASS_NAME not in data_handler.data:
raise PlatformNotReady
async def get_entities(): async def get_entities():
"""Retrieve Netatmo entities.""" """Retrieve Netatmo entities."""
await data_handler.register_data_class(
CAMERA_DATA_CLASS_NAME, CAMERA_DATA_CLASS_NAME, None
)
entities = [] entities = []
all_cameras = [] all_cameras = []
if CAMERA_DATA_CLASS_NAME not in data_handler.data:
raise PlatformNotReady
try: try:
for home in data_handler.data[CAMERA_DATA_CLASS_NAME].cameras.values(): for home in data_handler.data[CAMERA_DATA_CLASS_NAME].cameras.values():
for camera in home.values(): for camera in home.values():

View File

@ -20,6 +20,7 @@ from homeassistant.const import (
TEMP_CELSIUS, TEMP_CELSIUS,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.device_registry import async_entries_for_config_entry from homeassistant.helpers.device_registry import async_entries_for_config_entry
from homeassistant.helpers.dispatcher import ( from homeassistant.helpers.dispatcher import (
async_dispatcher_connect, async_dispatcher_connect,
@ -129,14 +130,25 @@ async def async_setup_entry(hass, entry, async_add_entities):
"""Set up the Netatmo weather and homecoach platform.""" """Set up the Netatmo weather and homecoach platform."""
data_handler = hass.data[DOMAIN][entry.entry_id][DATA_HANDLER] data_handler = hass.data[DOMAIN][entry.entry_id][DATA_HANDLER]
await data_handler.register_data_class(
WEATHERSTATION_DATA_CLASS_NAME, WEATHERSTATION_DATA_CLASS_NAME, None
)
await data_handler.register_data_class(
HOMECOACH_DATA_CLASS_NAME, HOMECOACH_DATA_CLASS_NAME, None
)
async def find_entities(data_class_name): async def find_entities(data_class_name):
"""Find all entities.""" """Find all entities."""
await data_handler.register_data_class(data_class_name, data_class_name, None) if data_class_name not in data_handler.data:
raise PlatformNotReady
all_module_infos = {} all_module_infos = {}
data = data_handler.data data = data_handler.data
if not data.get(data_class_name): if data_class_name not in data:
return []
if data[data_class_name] is None:
return [] return []
data_class = data[data_class_name] data_class = data[data_class_name]
@ -174,6 +186,8 @@ async def async_setup_entry(hass, entry, async_add_entities):
NetatmoSensor(data_handler, data_class_name, module, condition) NetatmoSensor(data_handler, data_class_name, module, condition)
) )
await data_handler.unregister_data_class(data_class_name, None)
return entities return entities
for data_class_name in [ for data_class_name in [

View File

@ -2,7 +2,7 @@
"domain": "opentherm_gw", "domain": "opentherm_gw",
"name": "OpenTherm Gateway", "name": "OpenTherm Gateway",
"documentation": "https://www.home-assistant.io/integrations/opentherm_gw", "documentation": "https://www.home-assistant.io/integrations/opentherm_gw",
"requirements": ["pyotgw==1.0b1"], "requirements": ["pyotgw==1.1b1"],
"codeowners": ["@mvn23"], "codeowners": ["@mvn23"],
"config_flow": true "config_flow": true
} }

View File

@ -3,7 +3,7 @@
"name": "Philips TV", "name": "Philips TV",
"documentation": "https://www.home-assistant.io/integrations/philips_js", "documentation": "https://www.home-assistant.io/integrations/philips_js",
"requirements": [ "requirements": [
"ha-philipsjs==2.3.0" "ha-philipsjs==2.3.1"
], ],
"codeowners": [ "codeowners": [
"@elupus" "@elupus"

View File

@ -13,10 +13,9 @@ from homeassistant.exceptions import (
TemplateError, TemplateError,
Unauthorized, Unauthorized,
) )
from homeassistant.helpers import config_validation as cv, entity from homeassistant.helpers import config_validation as cv, entity, template
from homeassistant.helpers.event import TrackTemplate, async_track_template_result from homeassistant.helpers.event import TrackTemplate, async_track_template_result
from homeassistant.helpers.service import async_get_all_descriptions from homeassistant.helpers.service import async_get_all_descriptions
from homeassistant.helpers.template import Template
from homeassistant.loader import IntegrationNotFound, async_get_integration from homeassistant.loader import IntegrationNotFound, async_get_integration
from . import const, decorators, messages from . import const, decorators, messages
@ -132,6 +131,11 @@ async def handle_call_service(hass, connection, msg):
if msg["domain"] == HASS_DOMAIN and msg["service"] in ["restart", "stop"]: if msg["domain"] == HASS_DOMAIN and msg["service"] in ["restart", "stop"]:
blocking = False blocking = False
# We do not support templates.
target = msg.get("target")
if template.is_complex(target):
raise vol.Invalid("Templates are not supported here")
try: try:
context = connection.context(msg) context = connection.context(msg)
await hass.services.async_call( await hass.services.async_call(
@ -140,7 +144,7 @@ async def handle_call_service(hass, connection, msg):
msg.get("service_data"), msg.get("service_data"),
blocking, blocking,
context, context,
target=msg.get("target"), target=target,
) )
connection.send_message( connection.send_message(
messages.result_message(msg["id"], {"context": context}) messages.result_message(msg["id"], {"context": context})
@ -256,14 +260,14 @@ def handle_ping(hass, connection, msg):
async def handle_render_template(hass, connection, msg): async def handle_render_template(hass, connection, msg):
"""Handle render_template command.""" """Handle render_template command."""
template_str = msg["template"] template_str = msg["template"]
template = Template(template_str, hass) template_obj = template.Template(template_str, hass)
variables = msg.get("variables") variables = msg.get("variables")
timeout = msg.get("timeout") timeout = msg.get("timeout")
info = None info = None
if timeout: if timeout:
try: try:
timed_out = await template.async_render_will_timeout(timeout) timed_out = await template_obj.async_render_will_timeout(timeout)
except TemplateError as ex: except TemplateError as ex:
connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR, str(ex)) connection.send_error(msg["id"], const.ERR_TEMPLATE_ERROR, str(ex))
return return
@ -294,7 +298,7 @@ async def handle_render_template(hass, connection, msg):
try: try:
info = async_track_template_result( info = async_track_template_result(
hass, hass,
[TrackTemplate(template, variables)], [TrackTemplate(template_obj, variables)],
_template_listener, _template_listener,
raise_on_template_error=True, raise_on_template_error=True,
) )

View File

@ -3,7 +3,7 @@
"name": "Z-Wave JS", "name": "Z-Wave JS",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/zwave_js", "documentation": "https://www.home-assistant.io/integrations/zwave_js",
"requirements": ["zwave-js-server-python==0.21.0"], "requirements": ["zwave-js-server-python==0.21.1"],
"codeowners": ["@home-assistant/z-wave"], "codeowners": ["@home-assistant/z-wave"],
"dependencies": ["http", "websocket_api"] "dependencies": ["http", "websocket_api"]
} }

View File

@ -70,10 +70,15 @@ set_config_parameter:
refresh_value: refresh_value:
name: Refresh value(s) of a Z-Wave entity name: Refresh value(s) of a Z-Wave entity
description: Force update value(s) for a Z-Wave entity description: Force update value(s) for a Z-Wave entity
target: fields:
entity_id:
name: Entity
description: Entity whose value(s) should be refreshed
required: true
example: sensor.family_room_motion
selector:
entity: entity:
integration: zwave_js integration: zwave_js
fields:
refresh_all_values: refresh_all_values:
name: Refresh all values? name: Refresh all values?
description: Whether to refresh all values (true) or just the primary value (false) description: Whether to refresh all values (true) or just the primary value (false)

View File

@ -1,7 +1,7 @@
"""Constants used by Home Assistant components.""" """Constants used by Home Assistant components."""
MAJOR_VERSION = 2021 MAJOR_VERSION = 2021
MINOR_VERSION = 3 MINOR_VERSION = 3
PATCH_VERSION = "1" PATCH_VERSION = "2"
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__ = f"{__short_version__}.{PATCH_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER = (3, 8, 0) REQUIRED_PYTHON_VER = (3, 8, 0)

View File

@ -15,7 +15,7 @@ defusedxml==0.6.0
distro==1.5.0 distro==1.5.0
emoji==1.2.0 emoji==1.2.0
hass-nabucasa==0.41.0 hass-nabucasa==0.41.0
home-assistant-frontend==20210302.4 home-assistant-frontend==20210302.5
httpx==0.16.1 httpx==0.16.1
jinja2>=2.11.3 jinja2>=2.11.3
netdisco==2.8.2 netdisco==2.8.2

View File

@ -245,7 +245,7 @@ alpha_vantage==2.3.1
ambiclimate==0.2.1 ambiclimate==0.2.1
# homeassistant.components.amcrest # homeassistant.components.amcrest
amcrest==1.7.0 amcrest==1.7.1
# homeassistant.components.androidtv # homeassistant.components.androidtv
androidtv[async]==0.0.57 androidtv[async]==0.0.57
@ -721,7 +721,7 @@ guppy3==3.1.0
ha-ffmpeg==3.0.2 ha-ffmpeg==3.0.2
# homeassistant.components.philips_js # homeassistant.components.philips_js
ha-philipsjs==2.3.0 ha-philipsjs==2.3.1
# homeassistant.components.habitica # homeassistant.components.habitica
habitipy==0.2.0 habitipy==0.2.0
@ -763,7 +763,7 @@ hole==0.5.1
holidays==0.10.5.2 holidays==0.10.5.2
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20210302.4 home-assistant-frontend==20210302.5
# homeassistant.components.zwave # homeassistant.components.zwave
homeassistant-pyozw==0.1.10 homeassistant-pyozw==0.1.10
@ -1603,7 +1603,7 @@ pyoppleio==1.0.5
pyota==2.0.5 pyota==2.0.5
# homeassistant.components.opentherm_gw # homeassistant.components.opentherm_gw
pyotgw==1.0b1 pyotgw==1.1b1
# homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.notify
# homeassistant.auth.mfa_modules.totp # homeassistant.auth.mfa_modules.totp
@ -2397,4 +2397,4 @@ zigpy==0.32.0
zm-py==0.5.2 zm-py==0.5.2
# homeassistant.components.zwave_js # homeassistant.components.zwave_js
zwave-js-server-python==0.21.0 zwave-js-server-python==0.21.1

View File

@ -382,7 +382,7 @@ guppy3==3.1.0
ha-ffmpeg==3.0.2 ha-ffmpeg==3.0.2
# homeassistant.components.philips_js # homeassistant.components.philips_js
ha-philipsjs==2.3.0 ha-philipsjs==2.3.1
# homeassistant.components.habitica # homeassistant.components.habitica
habitipy==0.2.0 habitipy==0.2.0
@ -412,7 +412,7 @@ hole==0.5.1
holidays==0.10.5.2 holidays==0.10.5.2
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20210302.4 home-assistant-frontend==20210302.5
# homeassistant.components.zwave # homeassistant.components.zwave
homeassistant-pyozw==0.1.10 homeassistant-pyozw==0.1.10
@ -851,7 +851,7 @@ pyopenuv==1.0.9
pyopnsense==0.2.0 pyopnsense==0.2.0
# homeassistant.components.opentherm_gw # homeassistant.components.opentherm_gw
pyotgw==1.0b1 pyotgw==1.1b1
# homeassistant.auth.mfa_modules.notify # homeassistant.auth.mfa_modules.notify
# homeassistant.auth.mfa_modules.totp # homeassistant.auth.mfa_modules.totp
@ -1234,4 +1234,4 @@ zigpy-znp==0.4.0
zigpy==0.32.0 zigpy==0.32.0
# homeassistant.components.zwave_js # homeassistant.components.zwave_js
zwave-js-server-python==0.21.0 zwave-js-server-python==0.21.1

View File

@ -189,6 +189,7 @@ async def test_hue_activate_scene(hass, mock_api):
assert len(mock_api.mock_requests) == 3 assert len(mock_api.mock_requests) == 3
assert mock_api.mock_requests[2]["json"]["scene"] == "scene_1" assert mock_api.mock_requests[2]["json"]["scene"] == "scene_1"
assert "transitiontime" not in mock_api.mock_requests[2]["json"]
assert mock_api.mock_requests[2]["path"] == "groups/group_1/action" assert mock_api.mock_requests[2]["path"] == "groups/group_1/action"

View File

@ -21,13 +21,7 @@ from tests.common import MockEntity, MockEntityPlatform, async_mock_service
async def test_call_service(hass, websocket_client): async def test_call_service(hass, websocket_client):
"""Test call service command.""" """Test call service command."""
calls = [] calls = async_mock_service(hass, "domain_test", "test_service")
@callback
def service_call(call):
calls.append(call)
hass.services.async_register("domain_test", "test_service", service_call)
await websocket_client.send_json( await websocket_client.send_json(
{ {
@ -54,13 +48,7 @@ async def test_call_service(hass, websocket_client):
async def test_call_service_target(hass, websocket_client): async def test_call_service_target(hass, websocket_client):
"""Test call service command with target.""" """Test call service command with target."""
calls = [] calls = async_mock_service(hass, "domain_test", "test_service")
@callback
def service_call(call):
calls.append(call)
hass.services.async_register("domain_test", "test_service", service_call)
await websocket_client.send_json( await websocket_client.send_json(
{ {
@ -93,6 +81,28 @@ async def test_call_service_target(hass, websocket_client):
} }
async def test_call_service_target_template(hass, websocket_client):
"""Test call service command with target does not allow template."""
await websocket_client.send_json(
{
"id": 5,
"type": "call_service",
"domain": "domain_test",
"service": "test_service",
"service_data": {"hello": "world"},
"target": {
"entity_id": "{{ 1 }}",
},
}
)
msg = await websocket_client.receive_json()
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert not msg["success"]
assert msg["error"]["code"] == const.ERR_INVALID_FORMAT
async def test_call_service_not_found(hass, websocket_client): async def test_call_service_not_found(hass, websocket_client):
"""Test call service command.""" """Test call service command."""
await websocket_client.send_json( await websocket_client.send_json(
@ -232,7 +242,6 @@ async def test_call_service_error(hass, websocket_client):
) )
msg = await websocket_client.receive_json() msg = await websocket_client.receive_json()
print(msg)
assert msg["id"] == 5 assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT assert msg["type"] == const.TYPE_RESULT
assert msg["success"] is False assert msg["success"] is False
@ -249,7 +258,6 @@ async def test_call_service_error(hass, websocket_client):
) )
msg = await websocket_client.receive_json() msg = await websocket_client.receive_json()
print(msg)
assert msg["id"] == 6 assert msg["id"] == 6
assert msg["type"] == const.TYPE_RESULT assert msg["type"] == const.TYPE_RESULT
assert msg["success"] is False assert msg["success"] is False