mirror of
https://github.com/home-assistant/core.git
synced 2025-07-29 16:17:20 +00:00
Merge pull request #47490 from home-assistant/rc
This commit is contained in:
commit
1145c30c4b
@ -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"]
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -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),
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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(
|
||||||
|
@ -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]]
|
||||||
|
@ -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])},
|
||||||
|
@ -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:
|
||||||
|
@ -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(
|
||||||
|
@ -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():
|
||||||
|
@ -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 [
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
@ -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"]
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user