Add vibration triggers (#176408)

This commit is contained in:
Niels
2026-07-15 16:10:27 +02:00
committed by GitHub
parent 8806b74737
commit 2f97d5b950
13 changed files with 324 additions and 0 deletions
Generated
+2
View File
@@ -1967,6 +1967,8 @@ CLAUDE.md @home-assistant/core
/tests/components/version/ @ludeeus
/homeassistant/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja @iprak @sapuseven
/tests/components/vesync/ @markperdue @webdjoe @thegardenmonkey @cdnninja @iprak @sapuseven
/homeassistant/components/vibration/ @home-assistant/core
/tests/components/vibration/ @home-assistant/core
/homeassistant/components/vicare/ @CFenner @lackas
/tests/components/vicare/ @CFenner @lackas
/homeassistant/components/victron_ble/ @rajlaud
+1
View File
@@ -264,6 +264,7 @@ DEFAULT_INTEGRATIONS = {
"occupancy",
"power",
"temperature",
"vibration",
"window",
}
DEFAULT_INTEGRATIONS_RECOVERY_MODE = {
@@ -0,0 +1,15 @@
"""Integration for vibration triggers."""
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
DOMAIN = "vibration"
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
__all__ = []
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the component."""
return True
@@ -0,0 +1,10 @@
{
"triggers": {
"cleared": {
"trigger": "mdi:vibrate-off"
},
"detected": {
"trigger": "mdi:vibrate"
}
}
}
@@ -0,0 +1,8 @@
{
"domain": "vibration",
"name": "Vibration",
"codeowners": ["@home-assistant/core"],
"documentation": "https://www.home-assistant.io/integrations/vibration",
"integration_type": "system",
"quality_scale": "internal"
}
@@ -0,0 +1,33 @@
{
"common": {
"trigger_behavior_name": "Trigger when",
"trigger_for_name": "For at least"
},
"title": "Vibration",
"triggers": {
"cleared": {
"description": "Triggers when one or more vibration sensors stop detecting vibration.",
"fields": {
"behavior": {
"name": "[%key:component::vibration::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::vibration::common::trigger_for_name%]"
}
},
"name": "Vibration cleared"
},
"detected": {
"description": "Triggers when one or more vibration sensors start detecting vibration.",
"fields": {
"behavior": {
"name": "[%key:component::vibration::common::trigger_behavior_name%]"
},
"for": {
"name": "[%key:component::vibration::common::trigger_for_name%]"
}
},
"name": "Vibration detected"
}
}
}
@@ -0,0 +1,24 @@
"""Provides triggers for vibration."""
from homeassistant.components.binary_sensor import (
DOMAIN as BINARY_SENSOR_DOMAIN,
BinarySensorDeviceClass,
)
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers.automation import DomainSpec
from homeassistant.helpers.trigger import Trigger, make_entity_target_state_trigger
VIBRATION_DOMAIN_SPECS: dict[str, DomainSpec] = {
BINARY_SENSOR_DOMAIN: DomainSpec(device_class=BinarySensorDeviceClass.VIBRATION),
}
TRIGGERS: dict[str, type[Trigger]] = {
"detected": make_entity_target_state_trigger(VIBRATION_DOMAIN_SPECS, STATE_ON),
"cleared": make_entity_target_state_trigger(VIBRATION_DOMAIN_SPECS, STATE_OFF),
}
async def async_get_triggers(hass: HomeAssistant) -> dict[str, type[Trigger]]:
"""Return the triggers for vibration."""
return TRIGGERS
@@ -0,0 +1,26 @@
.trigger_common_fields: &trigger_common_fields
behavior:
required: true
default: each
selector:
automation_behavior:
mode: trigger
for:
required: true
default: 00:00:00
selector:
duration:
detected:
fields: *trigger_common_fields
target:
entity:
- domain: binary_sensor
device_class: vibration
cleared:
fields: *trigger_common_fields
target:
entity:
- domain: binary_sensor
device_class: vibration
+1
View File
@@ -126,6 +126,7 @@ NO_IOT_CLASS = [
"temperature",
"timer",
"trace",
"vibration",
"web_rtc",
"webhook",
"websocket_api",
+1
View File
@@ -2076,6 +2076,7 @@ NO_QUALITY_SCALE = [
"timer",
"trace",
"usage_prediction",
"vibration",
"web_rtc",
"webhook",
"websocket_api",
+1
View File
@@ -0,0 +1 @@
"""Tests for the vibration integration."""
+200
View File
@@ -0,0 +1,200 @@
"""Test vibration trigger."""
from typing import Any
import pytest
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.const import ATTR_DEVICE_CLASS, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from tests.components.common import (
TriggerStateDescription,
assert_trigger_behavior_all,
assert_trigger_behavior_each,
assert_trigger_behavior_first,
assert_trigger_options_supported,
parametrize_target_entities,
parametrize_trigger_states,
target_entities,
)
@pytest.fixture
async def target_binary_sensors(hass: HomeAssistant) -> dict[str, list[str]]:
"""Create multiple binary sensor entities associated with different targets."""
return await target_entities(hass, "binary_sensor")
@pytest.mark.parametrize(
("trigger_key", "base_options", "supports_behavior", "supports_duration"),
[
("vibration.detected", {}, True, True),
("vibration.cleared", {}, True, True),
],
)
async def test_vibration_trigger_options_validation(
hass: HomeAssistant,
trigger_key: str,
base_options: dict[str, Any] | None,
supports_behavior: bool,
supports_duration: bool,
) -> None:
"""Test that vibration triggers support the expected options."""
await assert_trigger_options_supported(
hass,
trigger_key,
base_options,
supports_behavior=supports_behavior,
supports_duration=supports_duration,
)
@pytest.mark.parametrize(
("trigger_target_config", "entity_id", "entities_in_target"),
parametrize_target_entities("binary_sensor"),
)
@pytest.mark.parametrize(
("trigger", "trigger_options", "states"),
[
*parametrize_trigger_states(
trigger="vibration.detected",
target_states=[STATE_ON],
other_states=[STATE_OFF],
required_filter_attributes={
ATTR_DEVICE_CLASS: BinarySensorDeviceClass.VIBRATION
},
trigger_from_none=False,
),
*parametrize_trigger_states(
trigger="vibration.cleared",
target_states=[STATE_OFF],
other_states=[STATE_ON],
required_filter_attributes={
ATTR_DEVICE_CLASS: BinarySensorDeviceClass.VIBRATION
},
trigger_from_none=False,
),
],
)
async def test_vibration_trigger_binary_sensor_behavior_each(
hass: HomeAssistant,
target_binary_sensors: dict[str, list[str]],
trigger_target_config: dict,
entity_id: str,
entities_in_target: int,
trigger: str,
trigger_options: dict[str, Any],
states: list[TriggerStateDescription],
) -> None:
"""Test vibration trigger fires for binary_sensor entities with device_class vibration."""
await assert_trigger_behavior_each(
hass,
target_entities=target_binary_sensors,
trigger_target_config=trigger_target_config,
entity_id=entity_id,
entities_in_target=entities_in_target,
trigger=trigger,
trigger_options=trigger_options,
states=states,
)
@pytest.mark.parametrize(
("trigger_target_config", "entity_id", "entities_in_target"),
parametrize_target_entities("binary_sensor"),
)
@pytest.mark.parametrize(
("trigger", "trigger_options", "states"),
[
*parametrize_trigger_states(
trigger="vibration.detected",
target_states=[STATE_ON],
other_states=[STATE_OFF],
required_filter_attributes={
ATTR_DEVICE_CLASS: BinarySensorDeviceClass.VIBRATION
},
trigger_from_none=False,
),
*parametrize_trigger_states(
trigger="vibration.cleared",
target_states=[STATE_OFF],
other_states=[STATE_ON],
required_filter_attributes={
ATTR_DEVICE_CLASS: BinarySensorDeviceClass.VIBRATION
},
trigger_from_none=False,
),
],
)
async def test_vibration_trigger_binary_sensor_behavior_first(
hass: HomeAssistant,
target_binary_sensors: dict[str, list[str]],
trigger_target_config: dict,
entity_id: str,
entities_in_target: int,
trigger: str,
trigger_options: dict[str, Any],
states: list[TriggerStateDescription],
) -> None:
"""Test vibration trigger fires on the first binary_sensor state change."""
await assert_trigger_behavior_first(
hass,
target_entities=target_binary_sensors,
trigger_target_config=trigger_target_config,
entity_id=entity_id,
entities_in_target=entities_in_target,
trigger=trigger,
trigger_options=trigger_options,
states=states,
)
@pytest.mark.parametrize(
("trigger_target_config", "entity_id", "entities_in_target"),
parametrize_target_entities("binary_sensor"),
)
@pytest.mark.parametrize(
("trigger", "trigger_options", "states"),
[
*parametrize_trigger_states(
trigger="vibration.detected",
target_states=[STATE_ON],
other_states=[STATE_OFF],
required_filter_attributes={
ATTR_DEVICE_CLASS: BinarySensorDeviceClass.VIBRATION
},
trigger_from_none=False,
),
*parametrize_trigger_states(
trigger="vibration.cleared",
target_states=[STATE_OFF],
other_states=[STATE_ON],
required_filter_attributes={
ATTR_DEVICE_CLASS: BinarySensorDeviceClass.VIBRATION
},
trigger_from_none=False,
),
],
)
async def test_vibration_trigger_binary_sensor_behavior_all(
hass: HomeAssistant,
target_binary_sensors: dict[str, list[str]],
trigger_target_config: dict,
entity_id: str,
entities_in_target: int,
trigger: str,
trigger_options: dict[str, Any],
states: list[TriggerStateDescription],
) -> None:
"""Test vibration trigger fires when all binary_sensors have changed state."""
await assert_trigger_behavior_all(
hass,
target_entities=target_binary_sensors,
trigger_target_config=trigger_target_config,
entity_id=entity_id,
entities_in_target=entities_in_target,
trigger=trigger,
trigger_options=trigger_options,
states=states,
)
+2
View File
@@ -100,6 +100,7 @@
'update',
'vacuum',
'valve',
'vibration',
'wake_word',
'water_heater',
'weather',
@@ -209,6 +210,7 @@
'update',
'vacuum',
'valve',
'vibration',
'wake_word',
'water_heater',
'weather',