mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Fix empty actions (#142292)
* Apply fix * Add tests for alarm button cover lock * update light * add number tests * test select * add switch tests * test vacuum * update lock test
This commit is contained in:
parent
8d62cb60a6
commit
c25f26a290
@ -214,7 +214,8 @@ class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity, Restore
|
||||
),
|
||||
(CONF_TRIGGER_ACTION, AlarmControlPanelEntityFeature.TRIGGER),
|
||||
):
|
||||
if action_config := config.get(action_id):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (action_config := config.get(action_id)) is not None:
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
self._attr_supported_features |= supported_feature
|
||||
|
||||
|
@ -120,7 +120,8 @@ class TemplateButtonEntity(TemplateEntity, ButtonEntity):
|
||||
"""Initialize the button."""
|
||||
super().__init__(hass, config=config, unique_id=unique_id)
|
||||
assert self._attr_name is not None
|
||||
if action := config.get(CONF_PRESS):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (action := config.get(CONF_PRESS)) is not None:
|
||||
self.add_script(CONF_PRESS, action, self._attr_name, DOMAIN)
|
||||
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
|
||||
self._attr_state = None
|
||||
|
@ -172,7 +172,8 @@ class CoverTemplate(TemplateEntity, CoverEntity):
|
||||
(POSITION_ACTION, CoverEntityFeature.SET_POSITION),
|
||||
(TILT_ACTION, TILT_FEATURES),
|
||||
):
|
||||
if action_config := config.get(action_id):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (action_config := config.get(action_id)) is not None:
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
self._attr_supported_features |= supported_feature
|
||||
|
||||
|
@ -157,7 +157,8 @@ class TemplateFan(TemplateEntity, FanEntity):
|
||||
CONF_SET_OSCILLATING_ACTION,
|
||||
CONF_SET_DIRECTION_ACTION,
|
||||
):
|
||||
if action_config := config.get(action_id):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (action_config := config.get(action_id)) is not None:
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
|
||||
self._state: bool | None = False
|
||||
|
@ -296,7 +296,8 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._supports_transition_template = config.get(CONF_SUPPORTS_TRANSITION)
|
||||
|
||||
for action_id in (CONF_ON_ACTION, CONF_OFF_ACTION, CONF_EFFECT_ACTION):
|
||||
if action_config := config.get(action_id):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (action_config := config.get(action_id)) is not None:
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
|
||||
self._state = False
|
||||
@ -323,7 +324,8 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
(CONF_RGBW_ACTION, ColorMode.RGBW),
|
||||
(CONF_RGBWW_ACTION, ColorMode.RGBWW),
|
||||
):
|
||||
if action_config := config.get(action_id):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (action_config := config.get(action_id)) is not None:
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
color_modes.add(color_mode)
|
||||
self._supported_color_modes = filter_supported_color_modes(color_modes)
|
||||
@ -333,7 +335,7 @@ class LightTemplate(TemplateEntity, LightEntity):
|
||||
self._color_mode = next(iter(self._supported_color_modes))
|
||||
|
||||
self._attr_supported_features = LightEntityFeature(0)
|
||||
if self._action_scripts.get(CONF_EFFECT_ACTION):
|
||||
if (self._action_scripts.get(CONF_EFFECT_ACTION)) is not None:
|
||||
self._attr_supported_features |= LightEntityFeature.EFFECT
|
||||
if self._supports_transition is True:
|
||||
self._attr_supported_features |= LightEntityFeature.TRANSITION
|
||||
|
@ -98,7 +98,8 @@ class TemplateLock(TemplateEntity, LockEntity):
|
||||
(CONF_UNLOCK, 0),
|
||||
(CONF_OPEN, LockEntityFeature.OPEN),
|
||||
):
|
||||
if action_config := config.get(action_id):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (action_config := config.get(action_id)) is not None:
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
self._attr_supported_features |= supported_feature
|
||||
self._code_format_template = config.get(CONF_CODE_FORMAT_TEMPLATE)
|
||||
|
@ -141,7 +141,8 @@ class TemplateSelect(TemplateEntity, SelectEntity):
|
||||
super().__init__(hass, config=config, unique_id=unique_id)
|
||||
assert self._attr_name is not None
|
||||
self._value_template = config[CONF_STATE]
|
||||
if select_option := config.get(CONF_SELECT_OPTION):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (select_option := config.get(CONF_SELECT_OPTION)) is not None:
|
||||
self.add_script(CONF_SELECT_OPTION, select_option, self._attr_name, DOMAIN)
|
||||
self._options_template = config[ATTR_OPTIONS]
|
||||
self._attr_assumed_state = self._optimistic = config.get(CONF_OPTIMISTIC, False)
|
||||
@ -197,7 +198,8 @@ class TriggerSelectEntity(TriggerEntity, SelectEntity):
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(hass, coordinator, config)
|
||||
if select_option := config.get(CONF_SELECT_OPTION):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (select_option := config.get(CONF_SELECT_OPTION)) is not None:
|
||||
self.add_script(
|
||||
CONF_SELECT_OPTION,
|
||||
select_option,
|
||||
|
@ -226,9 +226,10 @@ class SwitchTemplate(TemplateEntity, SwitchEntity, RestoreEntity):
|
||||
assert name is not None
|
||||
self._template = config.get(CONF_STATE)
|
||||
|
||||
if on_action := config.get(CONF_TURN_ON):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (on_action := config.get(CONF_TURN_ON)) is not None:
|
||||
self.add_script(CONF_TURN_ON, on_action, name, DOMAIN)
|
||||
if off_action := config.get(CONF_TURN_OFF):
|
||||
if (off_action := config.get(CONF_TURN_OFF)) is not None:
|
||||
self.add_script(CONF_TURN_OFF, off_action, name, DOMAIN)
|
||||
|
||||
self._state: bool | None = False
|
||||
|
@ -158,7 +158,8 @@ class TemplateVacuum(TemplateEntity, StateVacuumEntity):
|
||||
(SERVICE_LOCATE, VacuumEntityFeature.LOCATE),
|
||||
(SERVICE_SET_FAN_SPEED, VacuumEntityFeature.FAN_SPEED),
|
||||
):
|
||||
if action_config := config.get(action_id):
|
||||
# Scripts can be an empty list, therefore we need to check for None
|
||||
if (action_config := config.get(action_id)) is not None:
|
||||
self.add_script(action_id, action_config, name, DOMAIN)
|
||||
self._attr_supported_features |= supported_feature
|
||||
|
||||
|
@ -82,6 +82,15 @@ OPTIMISTIC_TEMPLATE_ALARM_CONFIG = {
|
||||
"data": {"code": "{{ this.entity_id }}"},
|
||||
},
|
||||
}
|
||||
EMPTY_ACTIONS = {
|
||||
"arm_away": [],
|
||||
"arm_home": [],
|
||||
"arm_night": [],
|
||||
"arm_vacation": [],
|
||||
"arm_custom_bypass": [],
|
||||
"disarm": [],
|
||||
"trigger": [],
|
||||
}
|
||||
|
||||
|
||||
TEMPLATE_ALARM_CONFIG = {
|
||||
@ -173,6 +182,12 @@ async def test_setup_config_entry(
|
||||
"panels": {"test_template_panel": OPTIMISTIC_TEMPLATE_ALARM_CONFIG},
|
||||
}
|
||||
},
|
||||
{
|
||||
"alarm_control_panel": {
|
||||
"platform": "template",
|
||||
"panels": {"test_template_panel": EMPTY_ACTIONS},
|
||||
}
|
||||
},
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("start_ha")
|
||||
|
@ -93,6 +93,45 @@ async def test_missing_optional_config(hass: HomeAssistant) -> None:
|
||||
_verify(hass, STATE_UNKNOWN)
|
||||
|
||||
|
||||
async def test_missing_emtpy_press_action_config(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test: missing optional template is ok."""
|
||||
with assert_setup_component(1, "template"):
|
||||
assert await setup.async_setup_component(
|
||||
hass,
|
||||
"template",
|
||||
{
|
||||
"template": {
|
||||
"button": {
|
||||
"press": [],
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
_verify(hass, STATE_UNKNOWN)
|
||||
|
||||
now = dt.datetime.now(dt.UTC)
|
||||
freezer.move_to(now)
|
||||
await hass.services.async_call(
|
||||
BUTTON_DOMAIN,
|
||||
SERVICE_PRESS,
|
||||
{CONF_ENTITY_ID: _TEST_BUTTON},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
_verify(
|
||||
hass,
|
||||
now.isoformat(),
|
||||
)
|
||||
|
||||
|
||||
async def test_missing_required_keys(hass: HomeAssistant) -> None:
|
||||
"""Test: missing required fields will fail."""
|
||||
with assert_setup_component(0, "template"):
|
||||
|
@ -9,6 +9,7 @@ from homeassistant.components.cover import (
|
||||
ATTR_POSITION,
|
||||
ATTR_TILT_POSITION,
|
||||
DOMAIN as COVER_DOMAIN,
|
||||
CoverEntityFeature,
|
||||
CoverState,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
@ -28,6 +29,7 @@ from homeassistant.const import (
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import assert_setup_component
|
||||
|
||||
@ -1123,3 +1125,50 @@ async def test_self_referencing_icon_with_no_template_is_not_a_loop(
|
||||
assert len(hass.states.async_all()) == 1
|
||||
|
||||
assert "Template loop detected" not in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("script", "supported_feature"),
|
||||
[
|
||||
("stop_cover", CoverEntityFeature.STOP),
|
||||
("set_cover_position", CoverEntityFeature.SET_POSITION),
|
||||
(
|
||||
"set_cover_tilt_position",
|
||||
CoverEntityFeature.OPEN_TILT
|
||||
| CoverEntityFeature.CLOSE_TILT
|
||||
| CoverEntityFeature.STOP_TILT
|
||||
| CoverEntityFeature.SET_TILT_POSITION,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_emtpy_action_config(
|
||||
hass: HomeAssistant, script: str, supported_feature: CoverEntityFeature
|
||||
) -> None:
|
||||
"""Test configuration with empty script."""
|
||||
with assert_setup_component(1, COVER_DOMAIN):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
COVER_DOMAIN,
|
||||
{
|
||||
COVER_DOMAIN: {
|
||||
"platform": "template",
|
||||
"covers": {
|
||||
"test_template_cover": {
|
||||
"open_cover": [],
|
||||
"close_cover": [],
|
||||
script: [],
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("cover.test_template_cover")
|
||||
assert (
|
||||
state.attributes["supported_features"]
|
||||
== CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | supported_feature
|
||||
)
|
||||
|
@ -556,6 +556,42 @@ async def setup_single_action_light(
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def setup_empty_action_light(
|
||||
hass: HomeAssistant,
|
||||
count: int,
|
||||
style: ConfigurationStyle,
|
||||
action: str,
|
||||
extra_config: dict,
|
||||
) -> None:
|
||||
"""Do setup of light integration."""
|
||||
if style == ConfigurationStyle.LEGACY:
|
||||
await async_setup_legacy_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
"test_template_light": {
|
||||
"turn_on": [],
|
||||
"turn_off": [],
|
||||
action: [],
|
||||
**extra_config,
|
||||
}
|
||||
},
|
||||
)
|
||||
elif style == ConfigurationStyle.MODERN:
|
||||
await async_setup_new_format(
|
||||
hass,
|
||||
count,
|
||||
{
|
||||
"name": "test_template_light",
|
||||
"turn_on": [],
|
||||
"turn_off": [],
|
||||
action: [],
|
||||
**extra_config,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def setup_light_with_effects(
|
||||
hass: HomeAssistant,
|
||||
@ -2404,3 +2440,82 @@ async def test_nested_unique_id(
|
||||
entry = entity_registry.async_get("light.test_b")
|
||||
assert entry
|
||||
assert entry.unique_id == "x-b"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("count", "extra_config"), [(1, {})])
|
||||
@pytest.mark.parametrize(
|
||||
"style",
|
||||
[
|
||||
ConfigurationStyle.LEGACY,
|
||||
ConfigurationStyle.MODERN,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("action", "color_mode"),
|
||||
[
|
||||
("set_level", ColorMode.BRIGHTNESS),
|
||||
("set_temperature", ColorMode.COLOR_TEMP),
|
||||
("set_hs", ColorMode.HS),
|
||||
("set_rgb", ColorMode.RGB),
|
||||
("set_rgbw", ColorMode.RGBW),
|
||||
("set_rgbww", ColorMode.RGBWW),
|
||||
],
|
||||
)
|
||||
async def test_empty_color_mode_action_config(
|
||||
hass: HomeAssistant,
|
||||
color_mode: ColorMode,
|
||||
setup_empty_action_light,
|
||||
) -> None:
|
||||
"""Test empty actions for color mode actions."""
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.attributes["supported_color_modes"] == [color_mode]
|
||||
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: "light.test_template_light"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.state == STATE_ON
|
||||
|
||||
await hass.services.async_call(
|
||||
light.DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "light.test_template_light"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("count"), [1])
|
||||
@pytest.mark.parametrize(
|
||||
("style", "extra_config"),
|
||||
[
|
||||
(
|
||||
ConfigurationStyle.LEGACY,
|
||||
{
|
||||
"effect_list_template": "{{ ['a'] }}",
|
||||
"effect_template": "{{ 'a' }}",
|
||||
},
|
||||
),
|
||||
(
|
||||
ConfigurationStyle.MODERN,
|
||||
{
|
||||
"effect_list": "{{ ['a'] }}",
|
||||
"effect": "{{ 'a' }}",
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("action", ["set_effect"])
|
||||
async def test_effect_with_empty_action(
|
||||
hass: HomeAssistant,
|
||||
setup_empty_action_light,
|
||||
) -> None:
|
||||
"""Test empty set_effect action."""
|
||||
state = hass.states.get("light.test_template_light")
|
||||
assert state.attributes["supported_features"] == LightEntityFeature.EFFECT
|
||||
|
@ -4,7 +4,7 @@ import pytest
|
||||
|
||||
from homeassistant import setup
|
||||
from homeassistant.components import lock
|
||||
from homeassistant.components.lock import LockState
|
||||
from homeassistant.components.lock import LockEntityFeature, LockState
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE,
|
||||
ATTR_ENTITY_ID,
|
||||
@ -15,6 +15,8 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
|
||||
from tests.common import assert_setup_component
|
||||
|
||||
OPTIMISTIC_LOCK_CONFIG = {
|
||||
"platform": "template",
|
||||
"lock": {
|
||||
@ -718,3 +720,50 @@ async def test_unique_id(hass: HomeAssistant) -> None:
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(hass.states.async_all("lock")) == 1
|
||||
|
||||
|
||||
async def test_emtpy_action_config(hass: HomeAssistant) -> None:
|
||||
"""Test configuration with empty script."""
|
||||
with assert_setup_component(1, lock.DOMAIN):
|
||||
assert await setup.async_setup_component(
|
||||
hass,
|
||||
lock.DOMAIN,
|
||||
{
|
||||
lock.DOMAIN: {
|
||||
"platform": "template",
|
||||
"value_template": "{{ 0 == 1 }}",
|
||||
"lock": [],
|
||||
"unlock": [],
|
||||
"open": [],
|
||||
"name": "test_template_lock",
|
||||
"optimistic": True,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("lock.test_template_lock")
|
||||
assert state.attributes["supported_features"] == LockEntityFeature.OPEN
|
||||
|
||||
await hass.services.async_call(
|
||||
lock.DOMAIN,
|
||||
lock.SERVICE_UNLOCK,
|
||||
{ATTR_ENTITY_ID: "lock.test_template_lock"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("lock.test_template_lock")
|
||||
assert state.state == LockState.UNLOCKED
|
||||
|
||||
await hass.services.async_call(
|
||||
lock.DOMAIN,
|
||||
lock.SERVICE_LOCK,
|
||||
{ATTR_ENTITY_ID: "lock.test_template_lock"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("lock.test_template_lock")
|
||||
assert state.state == LockState.LOCKED
|
||||
|
@ -1,8 +1,12 @@
|
||||
"""The tests for the Template number platform."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant import setup
|
||||
from homeassistant.components import number, template
|
||||
from homeassistant.components.input_number import (
|
||||
ATTR_VALUE as INPUT_NUMBER_ATTR_VALUE,
|
||||
DOMAIN as INPUT_NUMBER_DOMAIN,
|
||||
@ -18,6 +22,7 @@ from homeassistant.components.number import (
|
||||
)
|
||||
from homeassistant.components.template import DOMAIN
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_ICON,
|
||||
CONF_ENTITY_ID,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
@ -25,10 +30,14 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import Context, HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .conftest import ConfigurationStyle
|
||||
|
||||
from tests.common import MockConfigEntry, assert_setup_component, async_capture_events
|
||||
|
||||
_TEST_NUMBER = "number.template_number"
|
||||
_TEST_OBJECT_ID = "template_number"
|
||||
_TEST_NUMBER = f"number.{_TEST_OBJECT_ID}"
|
||||
# Represent for number's value
|
||||
_VALUE_INPUT_NUMBER = "input_number.value"
|
||||
# Represent for number's minimum
|
||||
@ -50,6 +59,38 @@ _VALUE_INPUT_NUMBER_CONFIG = {
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_modern_format(
|
||||
hass: HomeAssistant, count: int, number_config: dict[str, Any]
|
||||
) -> None:
|
||||
"""Do setup of number integration via new format."""
|
||||
config = {"template": {"number": number_config}}
|
||||
|
||||
with assert_setup_component(count, template.DOMAIN):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
template.DOMAIN,
|
||||
config,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def setup_number(
|
||||
hass: HomeAssistant,
|
||||
count: int,
|
||||
style: ConfigurationStyle,
|
||||
number_config: dict[str, Any],
|
||||
) -> None:
|
||||
"""Do setup of number integration."""
|
||||
if style == ConfigurationStyle.MODERN:
|
||||
await async_setup_modern_format(
|
||||
hass, count, {"name": _TEST_OBJECT_ID, **number_config}
|
||||
)
|
||||
|
||||
|
||||
async def test_setup_config_entry(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
@ -565,3 +606,36 @@ async def test_device_id(
|
||||
template_entity = entity_registry.async_get("number.my_template")
|
||||
assert template_entity is not None
|
||||
assert template_entity.device_id == device_entry.id
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("count", "number_config"),
|
||||
[
|
||||
(
|
||||
1,
|
||||
{
|
||||
"state": "{{ 1 }}",
|
||||
"set_value": [],
|
||||
"step": "{{ 1 }}",
|
||||
"optimistic": True,
|
||||
},
|
||||
)
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style",
|
||||
[
|
||||
ConfigurationStyle.MODERN,
|
||||
],
|
||||
)
|
||||
async def test_empty_action_config(hass: HomeAssistant, setup_number) -> None:
|
||||
"""Test configuration with empty script."""
|
||||
await hass.services.async_call(
|
||||
number.DOMAIN,
|
||||
number.SERVICE_SET_VALUE,
|
||||
{ATTR_ENTITY_ID: _TEST_NUMBER, "value": 4},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get(_TEST_NUMBER)
|
||||
assert float(state.state) == 4
|
||||
|
@ -1,8 +1,12 @@
|
||||
"""The tests for the Template select platform."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant import setup
|
||||
from homeassistant.components import select, template
|
||||
from homeassistant.components.input_select import (
|
||||
ATTR_OPTION as INPUT_SELECT_ATTR_OPTION,
|
||||
ATTR_OPTIONS as INPUT_SELECT_ATTR_OPTIONS,
|
||||
@ -17,17 +21,53 @@ from homeassistant.components.select import (
|
||||
SERVICE_SELECT_OPTION as SELECT_SERVICE_SELECT_OPTION,
|
||||
)
|
||||
from homeassistant.components.template import DOMAIN
|
||||
from homeassistant.const import ATTR_ICON, CONF_ENTITY_ID, STATE_UNKNOWN
|
||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_ICON, CONF_ENTITY_ID, STATE_UNKNOWN
|
||||
from homeassistant.core import Context, HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .conftest import ConfigurationStyle
|
||||
|
||||
from tests.common import MockConfigEntry, assert_setup_component, async_capture_events
|
||||
|
||||
_TEST_SELECT = "select.template_select"
|
||||
_TEST_OBJECT_ID = "template_select"
|
||||
_TEST_SELECT = f"select.{_TEST_OBJECT_ID}"
|
||||
# Represent for select's current_option
|
||||
_OPTION_INPUT_SELECT = "input_select.option"
|
||||
|
||||
|
||||
async def async_setup_modern_format(
|
||||
hass: HomeAssistant, count: int, select_config: dict[str, Any]
|
||||
) -> None:
|
||||
"""Do setup of select integration via new format."""
|
||||
config = {"template": {"select": select_config}}
|
||||
|
||||
with assert_setup_component(count, template.DOMAIN):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
template.DOMAIN,
|
||||
config,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def setup_select(
|
||||
hass: HomeAssistant,
|
||||
count: int,
|
||||
style: ConfigurationStyle,
|
||||
select_config: dict[str, Any],
|
||||
) -> None:
|
||||
"""Do setup of select integration."""
|
||||
if style == ConfigurationStyle.MODERN:
|
||||
await async_setup_modern_format(
|
||||
hass, count, {"name": _TEST_OBJECT_ID, **select_config}
|
||||
)
|
||||
|
||||
|
||||
async def test_setup_config_entry(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
@ -527,3 +567,36 @@ async def test_device_id(
|
||||
template_entity = entity_registry.async_get("select.my_template")
|
||||
assert template_entity is not None
|
||||
assert template_entity.device_id == device_entry.id
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("count", "select_config"),
|
||||
[
|
||||
(
|
||||
1,
|
||||
{
|
||||
"state": "{{ 'b' }}",
|
||||
"select_option": [],
|
||||
"options": "{{ ['a', 'b'] }}",
|
||||
"optimistic": True,
|
||||
},
|
||||
)
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style",
|
||||
[
|
||||
ConfigurationStyle.MODERN,
|
||||
],
|
||||
)
|
||||
async def test_empty_action_config(hass: HomeAssistant, setup_select) -> None:
|
||||
"""Test configuration with empty script."""
|
||||
await hass.services.async_call(
|
||||
select.DOMAIN,
|
||||
select.SERVICE_SELECT_OPTION,
|
||||
{ATTR_ENTITY_ID: _TEST_SELECT, "option": "a"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get(_TEST_SELECT)
|
||||
assert state.state == "a"
|
||||
|
@ -981,3 +981,49 @@ async def test_device_id(
|
||||
template_entity = entity_registry.async_get("switch.my_template")
|
||||
assert template_entity is not None
|
||||
assert template_entity.device_id == device_entry.id
|
||||
|
||||
|
||||
@pytest.mark.parametrize("count", [1])
|
||||
@pytest.mark.parametrize(
|
||||
("style", "switch_config"),
|
||||
[
|
||||
(
|
||||
ConfigurationStyle.LEGACY,
|
||||
{
|
||||
TEST_OBJECT_ID: {
|
||||
"turn_on": [],
|
||||
"turn_off": [],
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
ConfigurationStyle.MODERN,
|
||||
{
|
||||
"name": TEST_OBJECT_ID,
|
||||
"turn_on": [],
|
||||
"turn_off": [],
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_empty_action_config(hass: HomeAssistant, setup_switch) -> None:
|
||||
"""Test configuration with empty script."""
|
||||
await hass.services.async_call(
|
||||
switch.DOMAIN,
|
||||
switch.SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get(TEST_ENTITY_ID)
|
||||
assert state.state == STATE_ON
|
||||
|
||||
await hass.services.async_call(
|
||||
switch.DOMAIN,
|
||||
switch.SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: TEST_ENTITY_ID},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get(TEST_ENTITY_ID)
|
||||
assert state.state == STATE_OFF
|
||||
|
@ -1,18 +1,29 @@
|
||||
"""The tests for the Template vacuum platform."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant import setup
|
||||
from homeassistant.components.vacuum import ATTR_BATTERY_LEVEL, VacuumActivity
|
||||
from homeassistant.components import vacuum
|
||||
from homeassistant.components.vacuum import (
|
||||
ATTR_BATTERY_LEVEL,
|
||||
VacuumActivity,
|
||||
VacuumEntityFeature,
|
||||
)
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_component import async_update_entity
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .conftest import ConfigurationStyle
|
||||
|
||||
from tests.common import assert_setup_component
|
||||
from tests.components.vacuum import common
|
||||
|
||||
_TEST_VACUUM = "vacuum.test_vacuum"
|
||||
_TEST_OBJECT_ID = "test_vacuum"
|
||||
_TEST_VACUUM = f"vacuum.{_TEST_OBJECT_ID}"
|
||||
_STATE_INPUT_SELECT = "input_select.state"
|
||||
_SPOT_CLEANING_INPUT_BOOLEAN = "input_boolean.spot_cleaning"
|
||||
_LOCATING_INPUT_BOOLEAN = "input_boolean.locating"
|
||||
@ -20,6 +31,50 @@ _FAN_SPEED_INPUT_SELECT = "input_select.fan_speed"
|
||||
_BATTERY_LEVEL_INPUT_NUMBER = "input_number.battery_level"
|
||||
|
||||
|
||||
async def async_setup_legacy_format(
|
||||
hass: HomeAssistant, count: int, vacuum_config: dict[str, Any]
|
||||
) -> None:
|
||||
"""Do setup of number integration via new format."""
|
||||
config = {"vacuum": {"platform": "template", "vacuums": vacuum_config}}
|
||||
|
||||
with assert_setup_component(count, vacuum.DOMAIN):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
vacuum.DOMAIN,
|
||||
config,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def setup_vacuum(
|
||||
hass: HomeAssistant,
|
||||
count: int,
|
||||
style: ConfigurationStyle,
|
||||
vacuum_config: dict[str, Any],
|
||||
) -> None:
|
||||
"""Do setup of number integration."""
|
||||
if style == ConfigurationStyle.LEGACY:
|
||||
await async_setup_legacy_format(hass, count, vacuum_config)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def setup_test_vacuum_with_extra_config(
|
||||
hass: HomeAssistant,
|
||||
count: int,
|
||||
style: ConfigurationStyle,
|
||||
vacuum_config: dict[str, Any],
|
||||
extra_config: dict[str, Any],
|
||||
) -> None:
|
||||
"""Do setup of number integration."""
|
||||
config = {_TEST_OBJECT_ID: {**vacuum_config, **extra_config}}
|
||||
if style == ConfigurationStyle.LEGACY:
|
||||
await async_setup_legacy_format(hass, count, config)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("count", "domain"), [(1, "vacuum")])
|
||||
@pytest.mark.parametrize(
|
||||
("parm1", "parm2", "config"),
|
||||
@ -697,3 +752,71 @@ async def _register_components(hass: HomeAssistant) -> None:
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("count", [1])
|
||||
@pytest.mark.parametrize(
|
||||
("style", "vacuum_config"),
|
||||
[
|
||||
(
|
||||
ConfigurationStyle.LEGACY,
|
||||
{
|
||||
"start": [],
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("extra_config", "supported_features"),
|
||||
[
|
||||
(
|
||||
{
|
||||
"pause": [],
|
||||
},
|
||||
VacuumEntityFeature.PAUSE,
|
||||
),
|
||||
(
|
||||
{
|
||||
"stop": [],
|
||||
},
|
||||
VacuumEntityFeature.STOP,
|
||||
),
|
||||
(
|
||||
{
|
||||
"return_to_base": [],
|
||||
},
|
||||
VacuumEntityFeature.RETURN_HOME,
|
||||
),
|
||||
(
|
||||
{
|
||||
"clean_spot": [],
|
||||
},
|
||||
VacuumEntityFeature.CLEAN_SPOT,
|
||||
),
|
||||
(
|
||||
{
|
||||
"locate": [],
|
||||
},
|
||||
VacuumEntityFeature.LOCATE,
|
||||
),
|
||||
(
|
||||
{
|
||||
"set_fan_speed": [],
|
||||
},
|
||||
VacuumEntityFeature.FAN_SPEED,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_empty_action_config(
|
||||
hass: HomeAssistant,
|
||||
supported_features: VacuumEntityFeature,
|
||||
setup_test_vacuum_with_extra_config,
|
||||
) -> None:
|
||||
"""Test configuration with empty script."""
|
||||
await common.async_start(hass, _TEST_VACUUM)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(_TEST_VACUUM)
|
||||
assert state.attributes["supported_features"] == (
|
||||
VacuumEntityFeature.STATE | VacuumEntityFeature.START | supported_features
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user