Add config flow for platform button in Template (#120886)

This commit is contained in:
dougiteixeira 2024-07-09 16:20:18 -03:00 committed by GitHub
parent 5e56c27703
commit d783813ba0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 384 additions and 53 deletions

View File

@ -8,15 +8,22 @@ from typing import Any
import voluptuous as vol import voluptuous as vol
from homeassistant.components.button import DEVICE_CLASSES_SCHEMA, ButtonEntity from homeassistant.components.button import DEVICE_CLASSES_SCHEMA, ButtonEntity
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME, CONF_UNIQUE_ID from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_DEVICE_CLASS,
CONF_DEVICE_ID,
CONF_NAME,
CONF_UNIQUE_ID,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady from homeassistant.exceptions import PlatformNotReady
import homeassistant.helpers.config_validation as cv from homeassistant.helpers import config_validation as cv, selector
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.script import Script from homeassistant.helpers.script import Script
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import DOMAIN from .const import CONF_PRESS, DOMAIN
from .template_entity import ( from .template_entity import (
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA, TEMPLATE_ENTITY_AVAILABILITY_SCHEMA,
TEMPLATE_ENTITY_ICON_SCHEMA, TEMPLATE_ENTITY_ICON_SCHEMA,
@ -25,8 +32,6 @@ from .template_entity import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONF_PRESS = "press"
DEFAULT_NAME = "Template Button" DEFAULT_NAME = "Template Button"
DEFAULT_OPTIMISTIC = False DEFAULT_OPTIMISTIC = False
@ -43,6 +48,15 @@ BUTTON_SCHEMA = (
.extend(TEMPLATE_ENTITY_ICON_SCHEMA.schema) .extend(TEMPLATE_ENTITY_ICON_SCHEMA.schema)
) )
CONFIG_BUTTON_SCHEMA = vol.Schema(
{
vol.Optional(CONF_NAME): cv.template,
vol.Optional(CONF_PRESS): selector.ActionSelector(),
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(),
}
)
async def _async_create_entities( async def _async_create_entities(
hass: HomeAssistant, definitions: list[dict[str, Any]], unique_id_prefix: str | None hass: HomeAssistant, definitions: list[dict[str, Any]], unique_id_prefix: str | None
@ -76,6 +90,20 @@ async def async_setup_platform(
) )
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Initialize config entry."""
_options = dict(config_entry.options)
_options.pop("template_type")
validated_config = CONFIG_BUTTON_SCHEMA(_options)
async_add_entities(
[TemplateButtonEntity(hass, validated_config, config_entry.entry_id)]
)
class TemplateButtonEntity(TemplateEntity, ButtonEntity): class TemplateButtonEntity(TemplateEntity, ButtonEntity):
"""Representation of a template button.""" """Representation of a template button."""
@ -90,10 +118,19 @@ class TemplateButtonEntity(TemplateEntity, ButtonEntity):
"""Initialize the button.""" """Initialize the button."""
super().__init__(hass, config=config, unique_id=unique_id) super().__init__(hass, config=config, unique_id=unique_id)
assert self._attr_name is not None assert self._attr_name is not None
self._command_press = Script(hass, config[CONF_PRESS], self._attr_name, DOMAIN) self._command_press = (
Script(hass, config.get(CONF_PRESS), self._attr_name, DOMAIN)
if config.get(CONF_PRESS, None) is not None
else None
)
self._attr_device_class = config.get(CONF_DEVICE_CLASS) self._attr_device_class = config.get(CONF_DEVICE_CLASS)
self._attr_state = None self._attr_state = None
self._attr_device_info = async_device_info_to_link_from_device_id(
hass,
config.get(CONF_DEVICE_ID),
)
async def async_press(self) -> None: async def async_press(self) -> None:
"""Press the button.""" """Press the button."""
await self.async_run_script(self._command_press, context=self._context) if self._command_press:
await self.async_run_script(self._command_press, context=self._context)

View File

@ -10,6 +10,7 @@ import voluptuous as vol
from homeassistant.components import websocket_api from homeassistant.components import websocket_api
from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.button import ButtonDeviceClass
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
CONF_STATE_CLASS, CONF_STATE_CLASS,
DEVICE_CLASS_STATE_CLASSES, DEVICE_CLASS_STATE_CLASSES,
@ -36,7 +37,7 @@ from homeassistant.helpers.schema_config_entry_flow import (
) )
from .binary_sensor import async_create_preview_binary_sensor from .binary_sensor import async_create_preview_binary_sensor
from .const import DOMAIN from .const import CONF_PRESS, DOMAIN
from .sensor import async_create_preview_sensor from .sensor import async_create_preview_sensor
from .template_entity import TemplateEntity from .template_entity import TemplateEntity
@ -66,6 +67,22 @@ def generate_schema(domain: str, flow_type: str) -> vol.Schema:
), ),
} }
if domain == Platform.BUTTON:
schema |= {
vol.Optional(CONF_PRESS): selector.ActionSelector(),
}
if flow_type == "config":
schema |= {
vol.Optional(CONF_DEVICE_CLASS): selector.SelectSelector(
selector.SelectSelectorConfig(
options=[cls.value for cls in ButtonDeviceClass],
mode=selector.SelectSelectorMode.DROPDOWN,
translation_key="button_device_class",
sort=True,
),
)
}
if domain == Platform.SENSOR: if domain == Platform.SENSOR:
schema |= _SCHEMA_STATE | { schema |= _SCHEMA_STATE | {
vol.Optional(CONF_UNIT_OF_MEASUREMENT): selector.SelectSelector( vol.Optional(CONF_UNIT_OF_MEASUREMENT): selector.SelectSelector(
@ -195,6 +212,7 @@ def validate_user_input(
TEMPLATE_TYPES = [ TEMPLATE_TYPES = [
"binary_sensor", "binary_sensor",
"button",
"sensor", "sensor",
] ]
@ -205,6 +223,10 @@ CONFIG_FLOW = {
preview="template", preview="template",
validate_user_input=validate_user_input(Platform.BINARY_SENSOR), validate_user_input=validate_user_input(Platform.BINARY_SENSOR),
), ),
Platform.BUTTON: SchemaFlowFormStep(
config_schema(Platform.BUTTON),
validate_user_input=validate_user_input(Platform.BUTTON),
),
Platform.SENSOR: SchemaFlowFormStep( Platform.SENSOR: SchemaFlowFormStep(
config_schema(Platform.SENSOR), config_schema(Platform.SENSOR),
preview="template", preview="template",
@ -220,6 +242,10 @@ OPTIONS_FLOW = {
preview="template", preview="template",
validate_user_input=validate_user_input(Platform.BINARY_SENSOR), validate_user_input=validate_user_input(Platform.BINARY_SENSOR),
), ),
Platform.BUTTON: SchemaFlowFormStep(
options_schema(Platform.BUTTON),
validate_user_input=validate_user_input(Platform.BUTTON),
),
Platform.SENSOR: SchemaFlowFormStep( Platform.SENSOR: SchemaFlowFormStep(
options_schema(Platform.SENSOR), options_schema(Platform.SENSOR),
preview="template", preview="template",

View File

@ -32,4 +32,5 @@ CONF_AVAILABILITY = "availability"
CONF_ATTRIBUTES = "attributes" CONF_ATTRIBUTES = "attributes"
CONF_ATTRIBUTE_TEMPLATES = "attribute_templates" CONF_ATTRIBUTE_TEMPLATES = "attribute_templates"
CONF_PICTURE = "picture" CONF_PICTURE = "picture"
CONF_PRESS = "press"
CONF_OBJECT_ID = "object_id" CONF_OBJECT_ID = "object_id"

View File

@ -13,6 +13,18 @@
}, },
"title": "Template binary sensor" "title": "Template binary sensor"
}, },
"button": {
"data": {
"device_id": "[%key:common::config_flow::data::device%]",
"device_class": "[%key:component::template::config::step::sensor::data::device_class%]",
"name": "[%key:common::config_flow::data::name%]",
"press": "Actions on press"
},
"data_description": {
"device_id": "[%key:component::template::config::step::sensor::data_description::device_id%]"
},
"title": "Template button"
},
"sensor": { "sensor": {
"data": { "data": {
"device_id": "[%key:common::config_flow::data::device%]", "device_id": "[%key:common::config_flow::data::device%]",
@ -31,6 +43,7 @@
"description": "This helper allows you to create helper entities that define their state using a template.", "description": "This helper allows you to create helper entities that define their state using a template.",
"menu_options": { "menu_options": {
"binary_sensor": "Template a binary sensor", "binary_sensor": "Template a binary sensor",
"button": "Template a button",
"sensor": "Template a sensor" "sensor": "Template a sensor"
}, },
"title": "Template helper" "title": "Template helper"
@ -49,6 +62,16 @@
}, },
"title": "[%key:component::template::config::step::binary_sensor::title%]" "title": "[%key:component::template::config::step::binary_sensor::title%]"
}, },
"button": {
"data": {
"device_id": "[%key:common::config_flow::data::device%]",
"press": "[%key:component::template::config::step::button::data::press%]"
},
"data_description": {
"device_id": "[%key:component::template::config::step::sensor::data_description::device_id%]"
},
"title": "[%key:component::template::config::step::button::title%]"
},
"sensor": { "sensor": {
"data": { "data": {
"device_id": "[%key:common::config_flow::data::device%]", "device_id": "[%key:common::config_flow::data::device%]",
@ -97,6 +120,13 @@
"window": "[%key:component::binary_sensor::entity_component::window::name%]" "window": "[%key:component::binary_sensor::entity_component::window::name%]"
} }
}, },
"button_device_class": {
"options": {
"identify": "[%key:component::button::entity_component::identify::name%]",
"restart": "[%key:common::action::restart%]",
"update": "[%key:component::button::entity_component::update::name%]"
}
},
"sensor_device_class": { "sensor_device_class": {
"options": { "options": {
"apparent_power": "[%key:component::sensor::entity_component::apparent_power::name%]", "apparent_power": "[%key:component::sensor::entity_component::apparent_power::name%]",

View File

@ -0,0 +1,28 @@
# serializer version: 1
# name: test_setup_config_entry[config_entry_extra_options0]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'My template',
}),
'context': <ANY>,
'entity_id': 'button.my_template',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_setup_config_entry[config_entry_extra_options1]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'update',
'friendly_name': 'My template',
}),
'context': <ANY>,
'entity_id': 'button.my_template',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---

View File

@ -3,9 +3,12 @@
import datetime as dt import datetime as dt
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant import setup from homeassistant import setup
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
from homeassistant.components.template import DOMAIN
from homeassistant.components.template.button import DEFAULT_NAME from homeassistant.components.template.button import DEFAULT_NAME
from homeassistant.const import ( from homeassistant.const import (
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
@ -15,14 +18,58 @@ from homeassistant.const import (
STATE_UNKNOWN, STATE_UNKNOWN,
) )
from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from tests.common import assert_setup_component from tests.common import MockConfigEntry, assert_setup_component
_TEST_BUTTON = "button.template_button" _TEST_BUTTON = "button.template_button"
_TEST_OPTIONS_BUTTON = "button.test" _TEST_OPTIONS_BUTTON = "button.test"
@pytest.mark.parametrize(
"config_entry_extra_options",
[
{},
{
"device_class": "update",
},
],
)
async def test_setup_config_entry(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
config_entry_extra_options: dict[str, str],
) -> None:
"""Test the config flow."""
template_config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
"name": "My template",
"template_type": "button",
"press": [
{
"service": "input_boolean.toggle",
"metadata": {},
"data": {},
"target": {"entity_id": "input_boolean.test"},
}
],
}
| config_entry_extra_options,
title="My template",
)
template_config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(template_config_entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get("button.my_template")
assert state is not None
assert state == snapshot
async def test_missing_optional_config(hass: HomeAssistant) -> None: async def test_missing_optional_config(hass: HomeAssistant) -> None:
"""Test: missing optional template is ok.""" """Test: missing optional template is ok."""
with assert_setup_component(1, "template"): with assert_setup_component(1, "template"):
@ -197,3 +244,49 @@ def _verify(
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state == expected_value assert state.state == expected_value
assert state.attributes == attributes assert state.attributes == attributes
async def test_device_id(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test for device for button template."""
device_config_entry = MockConfigEntry()
device_config_entry.add_to_hass(hass)
device_entry = device_registry.async_get_or_create(
config_entry_id=device_config_entry.entry_id,
identifiers={("test", "identifier_test")},
connections={("mac", "30:31:32:33:34:35")},
)
await hass.async_block_till_done()
assert device_entry is not None
assert device_entry.id is not None
template_config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
options={
"name": "My template",
"template_type": "button",
"device_id": device_entry.id,
"press": [
{
"service": "input_boolean.toggle",
"metadata": {},
"data": {},
"target": {"entity_id": "input_boolean.test"},
}
],
},
title="My template",
)
template_config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(template_config_entry.entry_id)
await hass.async_block_till_done()
template_entity = entity_registry.async_get("button.my_template")
assert template_entity is not None
assert template_entity.device_id == device_entry.id

View File

@ -31,7 +31,9 @@ from tests.typing import WebSocketGenerator
[ [
( (
"binary_sensor", "binary_sensor",
"{{ states('binary_sensor.one') == 'on' or states('binary_sensor.two') == 'on' }}", {
"state": "{{ states('binary_sensor.one') == 'on' or states('binary_sensor.two') == 'on' }}"
},
"on", "on",
{"one": "on", "two": "off"}, {"one": "on", "two": "off"},
{}, {},
@ -41,7 +43,9 @@ from tests.typing import WebSocketGenerator
), ),
( (
"sensor", "sensor",
"{{ float(states('sensor.one')) + float(states('sensor.two')) }}", {
"state": "{{ float(states('sensor.one')) + float(states('sensor.two')) }}"
},
"50.0", "50.0",
{"one": "30.0", "two": "20.0"}, {"one": "30.0", "two": "20.0"},
{}, {},
@ -49,6 +53,34 @@ from tests.typing import WebSocketGenerator
{}, {},
{}, {},
), ),
(
"button",
{},
"unknown",
{"one": "30.0", "two": "20.0"},
{},
{
"device_class": "restart",
"press": [
{
"service": "input_boolean.toggle",
"target": {"entity_id": "input_boolean.test"},
"data": {},
}
],
},
{
"device_class": "restart",
"press": [
{
"service": "input_boolean.toggle",
"target": {"entity_id": "input_boolean.test"},
"data": {},
}
],
},
{},
),
], ],
) )
async def test_config_flow( async def test_config_flow(
@ -91,7 +123,7 @@ async def test_config_flow(
result["flow_id"], result["flow_id"],
{ {
"name": "My template", "name": "My template",
"state": state_template, **state_template,
**extra_input, **extra_input,
}, },
) )
@ -102,8 +134,8 @@ async def test_config_flow(
assert result["data"] == {} assert result["data"] == {}
assert result["options"] == { assert result["options"] == {
"name": "My template", "name": "My template",
"state": state_template,
"template_type": template_type, "template_type": template_type,
**state_template,
**extra_options, **extra_options,
} }
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
@ -112,8 +144,8 @@ async def test_config_flow(
assert config_entry.data == {} assert config_entry.data == {}
assert config_entry.options == { assert config_entry.options == {
"name": "My template", "name": "My template",
"state": state_template,
"template_type": template_type, "template_type": template_type,
**state_template,
**extra_options, **extra_options,
} }
@ -127,22 +159,36 @@ async def test_config_flow(
( (
"template_type", "template_type",
"state_template", "state_template",
"extra_input",
"extra_options",
), ),
[ [
( (
"sensor", "sensor",
"{{ 15 }}", {"state": "{{ 15 }}"},
{},
{},
), ),
( (
"binary_sensor", "binary_sensor",
"{{ false }}", {"state": "{{ false }}"},
{},
{},
),
(
"button",
{},
{},
{},
), ),
], ],
) )
async def test_config_flow_device( async def test_config_flow_device(
hass: HomeAssistant, hass: HomeAssistant,
template_type: str, template_type: str,
state_template: str, state_template: dict[str, Any],
extra_input: dict[str, Any],
extra_options: dict[str, Any],
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
) -> None: ) -> None:
"""Test remove the device registry configuration entry when the device changes.""" """Test remove the device registry configuration entry when the device changes."""
@ -180,8 +226,9 @@ async def test_config_flow_device(
result["flow_id"], result["flow_id"],
{ {
"name": "My template", "name": "My template",
"state": state_template,
"device_id": device_id, "device_id": device_id,
**state_template,
**extra_input,
}, },
) )
await hass.async_block_till_done() await hass.async_block_till_done()
@ -191,9 +238,10 @@ async def test_config_flow_device(
assert result["data"] == {} assert result["data"] == {}
assert result["options"] == { assert result["options"] == {
"name": "My template", "name": "My template",
"state": state_template,
"template_type": template_type, "template_type": template_type,
"device_id": device_id, "device_id": device_id,
**state_template,
**extra_options,
} }
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
@ -201,9 +249,10 @@ async def test_config_flow_device(
assert config_entry.data == {} assert config_entry.data == {}
assert config_entry.options == { assert config_entry.options == {
"name": "My template", "name": "My template",
"state": state_template,
"template_type": template_type, "template_type": template_type,
"device_id": device_id, "device_id": device_id,
**state_template,
**extra_options,
} }
@ -214,8 +263,8 @@ def get_suggested(schema, key):
if k.description is None or "suggested_value" not in k.description: if k.description is None or "suggested_value" not in k.description:
return None return None
return k.description["suggested_value"] return k.description["suggested_value"]
# Wanted key absent from schema # If the desired key is missing from the schema, return None
raise KeyError("Wanted key absent from schema") return None
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -231,8 +280,12 @@ def get_suggested(schema, key):
[ [
( (
"binary_sensor", "binary_sensor",
"{{ states('binary_sensor.one') == 'on' or states('binary_sensor.two') == 'on' }}", {
"{{ states('binary_sensor.one') == 'on' and states('binary_sensor.two') == 'on' }}", "state": "{{ states('binary_sensor.one') == 'on' or states('binary_sensor.two') == 'on' }}"
},
{
"state": "{{ states('binary_sensor.one') == 'on' and states('binary_sensor.two') == 'on' }}"
},
["on", "off"], ["on", "off"],
{"one": "on", "two": "off"}, {"one": "on", "two": "off"},
{}, {},
@ -240,13 +293,43 @@ def get_suggested(schema, key):
), ),
( (
"sensor", "sensor",
"{{ float(states('sensor.one')) + float(states('sensor.two')) }}", {
"{{ float(states('sensor.one')) - float(states('sensor.two')) }}", "state": "{{ float(states('sensor.one')) + float(states('sensor.two')) }}"
},
{
"state": "{{ float(states('sensor.one')) - float(states('sensor.two')) }}"
},
["50.0", "10.0"], ["50.0", "10.0"],
{"one": "30.0", "two": "20.0"}, {"one": "30.0", "two": "20.0"},
{}, {},
{}, {},
), ),
(
"button",
{},
{},
["unknown", "unknown"],
{"one": "30.0", "two": "20.0"},
{
"device_class": "restart",
"press": [
{
"service": "input_boolean.toggle",
"target": {"entity_id": "input_boolean.test"},
"data": {},
}
],
},
{
"press": [
{
"service": "input_boolean.toggle",
"target": {"entity_id": "input_boolean.test"},
"data": {},
}
],
},
),
], ],
) )
async def test_options( async def test_options(
@ -272,8 +355,8 @@ async def test_options(
domain=DOMAIN, domain=DOMAIN,
options={ options={
"name": "My template", "name": "My template",
"state": old_state_template,
"template_type": template_type, "template_type": template_type,
**old_state_template,
**extra_options, **extra_options,
}, },
title="My template", title="My template",
@ -291,25 +374,27 @@ async def test_options(
result = await hass.config_entries.options.async_init(config_entry.entry_id) result = await hass.config_entries.options.async_init(config_entry.entry_id)
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["step_id"] == template_type assert result["step_id"] == template_type
assert get_suggested(result["data_schema"].schema, "state") == old_state_template assert get_suggested(
result["data_schema"].schema, "state"
) == old_state_template.get("state")
assert "name" not in result["data_schema"].schema assert "name" not in result["data_schema"].schema
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], result["flow_id"],
user_input={"state": new_state_template, **options_options}, user_input={**new_state_template, **options_options},
) )
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == { assert result["data"] == {
"name": "My template", "name": "My template",
"state": new_state_template,
"template_type": template_type, "template_type": template_type,
**new_state_template,
**extra_options, **extra_options,
} }
assert config_entry.data == {} assert config_entry.data == {}
assert config_entry.options == { assert config_entry.options == {
"name": "My template", "name": "My template",
"state": new_state_template,
"template_type": template_type, "template_type": template_type,
**new_state_template,
**extra_options, **extra_options,
} }
assert config_entry.title == "My template" assert config_entry.title == "My template"
@ -943,22 +1028,36 @@ async def test_option_flow_sensor_preview_config_entry_removed(
( (
"template_type", "template_type",
"state_template", "state_template",
"extra_input",
"extra_options",
), ),
[ [
( (
"sensor", "sensor",
"{{ 15 }}", {"state": "{{ 15 }}"},
{},
{},
), ),
( (
"binary_sensor", "binary_sensor",
"{{ false }}", {"state": "{{ false }}"},
{},
{},
),
(
"button",
{},
{},
{},
), ),
], ],
) )
async def test_options_flow_change_device( async def test_options_flow_change_device(
hass: HomeAssistant, hass: HomeAssistant,
template_type: str, template_type: str,
state_template: str, state_template: dict[str, Any],
extra_input: dict[str, Any],
extra_options: dict[str, Any],
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
) -> None: ) -> None:
"""Test remove the device registry configuration entry when the device changes.""" """Test remove the device registry configuration entry when the device changes."""
@ -992,11 +1091,12 @@ async def test_options_flow_change_device(
domain=DOMAIN, domain=DOMAIN,
options={ options={
"template_type": template_type, "template_type": template_type,
"name": "Test", "name": "My template",
"state": state_template,
"device_id": device_id1, "device_id": device_id1,
**state_template,
**extra_options,
}, },
title="Sensor template", title="Template",
) )
template_config_entry.add_to_hass(hass) template_config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(template_config_entry.entry_id) assert await hass.config_entries.async_setup(template_config_entry.entry_id)
@ -1011,23 +1111,26 @@ async def test_options_flow_change_device(
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
"state": state_template,
"device_id": device_id2, "device_id": device_id2,
**state_template,
**extra_input,
}, },
) )
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == { assert result["data"] == {
"template_type": template_type, "template_type": template_type,
"name": "Test", "name": "My template",
"state": state_template,
"device_id": device_id2, "device_id": device_id2,
**state_template,
**extra_input,
} }
assert template_config_entry.data == {} assert template_config_entry.data == {}
assert template_config_entry.options == { assert template_config_entry.options == {
"template_type": template_type, "template_type": template_type,
"name": "Test", "name": "My template",
"state": state_template,
"device_id": device_id2, "device_id": device_id2,
**state_template,
**extra_options,
} }
# Remove link with device # Remove link with device
@ -1039,20 +1142,23 @@ async def test_options_flow_change_device(
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
"state": state_template, **state_template,
**extra_input,
}, },
) )
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == { assert result["data"] == {
"template_type": template_type, "template_type": template_type,
"name": "Test", "name": "My template",
"state": state_template, **state_template,
**extra_input,
} }
assert template_config_entry.data == {} assert template_config_entry.data == {}
assert template_config_entry.options == { assert template_config_entry.options == {
"template_type": template_type, "template_type": template_type,
"name": "Test", "name": "My template",
"state": state_template, **state_template,
**extra_options,
} }
# Change to link to device 1 # Change to link to device 1
@ -1064,21 +1170,24 @@ async def test_options_flow_change_device(
result = await hass.config_entries.options.async_configure( result = await hass.config_entries.options.async_configure(
result["flow_id"], result["flow_id"],
user_input={ user_input={
"state": state_template,
"device_id": device_id1, "device_id": device_id1,
**state_template,
**extra_input,
}, },
) )
assert result["type"] is FlowResultType.CREATE_ENTRY assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["data"] == { assert result["data"] == {
"template_type": template_type, "template_type": template_type,
"name": "Test", "name": "My template",
"state": state_template,
"device_id": device_id1, "device_id": device_id1,
**state_template,
**extra_input,
} }
assert template_config_entry.data == {} assert template_config_entry.data == {}
assert template_config_entry.options == { assert template_config_entry.options == {
"template_type": template_type, "template_type": template_type,
"name": "Test", "name": "My template",
"state": state_template,
"device_id": device_id1, "device_id": device_id1,
**state_template,
**extra_options,
} }

View File

@ -297,6 +297,13 @@ async def async_yaml_patch_helper(hass, filename):
"state": "{{1 == 2}}", "state": "{{1 == 2}}",
}, },
), ),
(
{
"template_type": "button",
"name": "My template",
},
{},
),
], ],
) )
async def test_change_device( async def test_change_device(