Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use entity registry id in toggle_entity device automations #94995

Merged
merged 3 commits into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions homeassistant/components/device_automation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
ATTR_ENTITY_ID,
CONF_DEVICE_ID,
CONF_DOMAIN,
CONF_ENTITY_ID,
CONF_PLATFORM,
)
from homeassistant.core import HomeAssistant, callback
Expand Down Expand Up @@ -340,6 +341,21 @@ def async_get_entity_registry_entry_or_raise(
return entry


@callback
def async_validate_entity_schema(
hass: HomeAssistant, config: ConfigType, schema: vol.Schema
) -> ConfigType:
"""Validate schema and resolve entity registry entry id to entity_id."""
config = schema(config)

registry = er.async_get(hass)
config[CONF_ENTITY_ID] = er.async_resolve_entity_id(
registry, config[CONF_ENTITY_ID]
)

return config


def handle_device_errors(
func: Callable[[HomeAssistant, ActiveConnection, dict[str, Any]], Awaitable[None]]
) -> Callable[
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/device_automation/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
{
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
vol.Required(CONF_TYPE): vol.In([CONF_CHANGED_STATES]),
vol.Optional(CONF_FOR): cv.positive_time_period_dict,
}
Expand Down Expand Up @@ -73,7 +73,7 @@ async def _async_get_automations(
{
**template,
"device_id": device_id,
"entity_id": entry.entity_id,
"entity_id": entry.id,
"domain": domain,
}
for template in automation_templates
Expand Down
12 changes: 12 additions & 0 deletions homeassistant/components/device_automation/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
DeviceAutomationType.TRIGGER: "TRIGGER_SCHEMA",
}

TOGGLE_ENTITY_DOMAINS = {"fan", "humidifier", "light", "remote", "switch"}


async def async_validate_device_automation_config(
hass: HomeAssistant,
Expand All @@ -43,6 +45,16 @@ async def async_validate_device_automation_config(
ConfigType, getattr(platform, STATIC_VALIDATOR[automation_type])(config)
)

# Bypass checks for toggle entity domains
if (
automation_type == DeviceAutomationType.ACTION
and validated_config[CONF_DOMAIN] in TOGGLE_ENTITY_DOMAINS
):
return cast(
ConfigType,
await getattr(platform, DYNAMIC_VALIDATOR[automation_type])(hass, config),
)

# Only call the dynamic validator if the referenced device exists and the relevant
# config entry is loaded
registry = dr.async_get(hass)
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/device_automation/toggle_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,22 +78,22 @@

ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
{
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
vol.Required(CONF_TYPE): vol.In(DEVICE_ACTION_TYPES),
}
)

CONDITION_SCHEMA = cv.DEVICE_CONDITION_BASE_SCHEMA.extend(
{
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
vol.Required(CONF_TYPE): vol.In([CONF_IS_OFF, CONF_IS_ON]),
vol.Optional(CONF_FOR): cv.positive_time_period_dict,
}
)

_TOGGLE_TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
{
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
vol.Required(CONF_TYPE): vol.In([CONF_TURNED_OFF, CONF_TURNED_ON]),
vol.Optional(CONF_FOR): cv.positive_time_period_dict,
}
Expand Down Expand Up @@ -196,7 +196,7 @@ async def _async_get_automations(
{
**template,
"device_id": device_id,
"entity_id": entry.entity_id,
"entity_id": entry.id,
"domain": domain,
}
for template in automation_templates
Expand Down
14 changes: 12 additions & 2 deletions homeassistant/components/humidifier/device_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

import voluptuous as vol

from homeassistant.components.device_automation import toggle_entity
from homeassistant.components.device_automation import (
async_validate_entity_schema,
toggle_entity,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_MODE,
Expand Down Expand Up @@ -41,7 +44,14 @@

ONOFF_SCHEMA = toggle_entity.ACTION_SCHEMA.extend({vol.Required(CONF_DOMAIN): DOMAIN})

ACTION_SCHEMA = vol.Any(SET_HUMIDITY_SCHEMA, SET_MODE_SCHEMA, ONOFF_SCHEMA)
_ACTION_SCHEMA = vol.Any(SET_HUMIDITY_SCHEMA, SET_MODE_SCHEMA, ONOFF_SCHEMA)


async def async_validate_action_config(
hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config."""
return async_validate_entity_schema(hass, config, _ACTION_SCHEMA)


async def async_get_actions(
Expand Down
23 changes: 18 additions & 5 deletions homeassistant/components/light/device_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

import voluptuous as vol

from homeassistant.components.device_automation import toggle_entity
from homeassistant.components.device_automation import (
async_get_entity_registry_entry_or_raise,
async_validate_entity_schema,
toggle_entity,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_DEVICE_ID,
Expand Down Expand Up @@ -37,9 +41,9 @@
TYPE_BRIGHTNESS_DECREASE = "brightness_decrease"
TYPE_FLASH = "flash"

ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
_ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
{
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
vol.Required(ATTR_ENTITY_ID): cv.entity_id_or_uuid,
vol.Required(CONF_DOMAIN): DOMAIN,
vol.Required(CONF_TYPE): vol.In(
toggle_entity.DEVICE_ACTION_TYPES
Expand All @@ -51,6 +55,13 @@
)


async def async_validate_action_config(
hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config."""
return async_validate_entity_schema(hass, config, _ACTION_SCHEMA)


async def async_call_action_from_config(
hass: HomeAssistant,
config: ConfigType,
Expand Down Expand Up @@ -126,13 +137,15 @@ async def async_get_action_capabilities(
if config[CONF_TYPE] != toggle_entity.CONF_TURN_ON:
return {}

entry = async_get_entity_registry_entry_or_raise(hass, config[CONF_ENTITY_ID])

try:
supported_color_modes = get_supported_color_modes(hass, config[ATTR_ENTITY_ID])
supported_color_modes = get_supported_color_modes(hass, entry.entity_id)
except HomeAssistantError:
supported_color_modes = None

try:
supported_features = get_supported_features(hass, config[ATTR_ENTITY_ID])
supported_features = get_supported_features(hass, entry.entity_id)
except HomeAssistantError:
supported_features = 0

Expand Down
14 changes: 12 additions & 2 deletions homeassistant/components/remote/device_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

import voluptuous as vol

from homeassistant.components.device_automation import toggle_entity
from homeassistant.components.device_automation import (
async_validate_entity_schema,
toggle_entity,
)
from homeassistant.const import CONF_DOMAIN
from homeassistant.core import Context, HomeAssistant
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
Expand All @@ -12,7 +15,14 @@

# mypy: disallow-any-generics

ACTION_SCHEMA = toggle_entity.ACTION_SCHEMA.extend({vol.Required(CONF_DOMAIN): DOMAIN})
_ACTION_SCHEMA = toggle_entity.ACTION_SCHEMA.extend({vol.Required(CONF_DOMAIN): DOMAIN})


async def async_validate_action_config(
hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config."""
return async_validate_entity_schema(hass, config, _ACTION_SCHEMA)


async def async_call_action_from_config(
Expand Down
14 changes: 12 additions & 2 deletions homeassistant/components/switch/device_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

import voluptuous as vol

from homeassistant.components.device_automation import toggle_entity
from homeassistant.components.device_automation import (
async_validate_entity_schema,
toggle_entity,
)
from homeassistant.const import CONF_DOMAIN
from homeassistant.core import Context, HomeAssistant
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
Expand All @@ -12,7 +15,14 @@

# mypy: disallow-any-generics

ACTION_SCHEMA = toggle_entity.ACTION_SCHEMA.extend({vol.Required(CONF_DOMAIN): DOMAIN})
_ACTION_SCHEMA = toggle_entity.ACTION_SCHEMA.extend({vol.Required(CONF_DOMAIN): DOMAIN})


async def async_validate_action_config(
hass: HomeAssistant, config: ConfigType
) -> ConfigType:
"""Validate config."""
return async_validate_entity_schema(hass, config, _ACTION_SCHEMA)


async def async_call_action_from_config(
Expand Down
22 changes: 11 additions & 11 deletions tests/components/device_automation/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,29 +114,29 @@ async def test_websocket_get_actions(
config_entry_id=config_entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
)
entity_registry.async_get_or_create(
entity_entry = entity_registry.async_get_or_create(
"fake_integration", "test", "5678", device_id=device_entry.id
)
expected_actions = [
{
"domain": "fake_integration",
"type": "turn_off",
"device_id": device_entry.id,
"entity_id": "fake_integration.test_5678",
"entity_id": entity_entry.id,
"metadata": {"secondary": False},
},
{
"domain": "fake_integration",
"type": "turn_on",
"device_id": device_entry.id,
"entity_id": "fake_integration.test_5678",
"entity_id": entity_entry.id,
"metadata": {"secondary": False},
},
{
"domain": "fake_integration",
"type": "toggle",
"device_id": device_entry.id,
"entity_id": "fake_integration.test_5678",
"entity_id": entity_entry.id,
"metadata": {"secondary": False},
},
]
Expand Down Expand Up @@ -169,7 +169,7 @@ async def test_websocket_get_conditions(
config_entry_id=config_entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
)
entity_registry.async_get_or_create(
entity_entry = entity_registry.async_get_or_create(
"fake_integration", "test", "5678", device_id=device_entry.id
)
expected_conditions = [
Expand All @@ -178,15 +178,15 @@ async def test_websocket_get_conditions(
"domain": "fake_integration",
"type": "is_off",
"device_id": device_entry.id,
"entity_id": "fake_integration.test_5678",
"entity_id": entity_entry.id,
"metadata": {"secondary": False},
},
{
"condition": "device",
"domain": "fake_integration",
"type": "is_on",
"device_id": device_entry.id,
"entity_id": "fake_integration.test_5678",
"entity_id": entity_entry.id,
"metadata": {"secondary": False},
},
]
Expand Down Expand Up @@ -223,7 +223,7 @@ async def test_websocket_get_triggers(
config_entry_id=config_entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
)
entity_registry.async_get_or_create(
entity_entry = entity_registry.async_get_or_create(
"fake_integration", "test", "5678", device_id=device_entry.id
)
expected_triggers = [
Expand All @@ -232,23 +232,23 @@ async def test_websocket_get_triggers(
"domain": "fake_integration",
"type": "changed_states",
"device_id": device_entry.id,
"entity_id": "fake_integration.test_5678",
"entity_id": entity_entry.id,
"metadata": {"secondary": False},
},
{
"platform": "device",
"domain": "fake_integration",
"type": "turned_off",
"device_id": device_entry.id,
"entity_id": "fake_integration.test_5678",
"entity_id": entity_entry.id,
"metadata": {"secondary": False},
},
{
"platform": "device",
"domain": "fake_integration",
"type": "turned_on",
"device_id": device_entry.id,
"entity_id": "fake_integration.test_5678",
"entity_id": entity_entry.id,
"metadata": {"secondary": False},
},
]
Expand Down
Loading