Fix Xiaomi-ble automations for multiple button devices (#109251)
parent
e11e54fd50
commit
697d4987c1
|
@ -21,8 +21,15 @@ XIAOMI_BLE_EVENT: Final = "xiaomi_ble_event"
|
|||
EVENT_CLASS_BUTTON: Final = "button"
|
||||
EVENT_CLASS_MOTION: Final = "motion"
|
||||
|
||||
BUTTON: Final = "button"
|
||||
DOUBLE_BUTTON: Final = "double_button"
|
||||
TRIPPLE_BUTTON: Final = "tripple_button"
|
||||
MOTION: Final = "motion"
|
||||
|
||||
BUTTON_PRESS: Final = "button_press"
|
||||
BUTTON_PRESS_DOUBLE_LONG: Final = "button_press_double_long"
|
||||
DOUBLE_BUTTON_PRESS_DOUBLE_LONG: Final = "double_button_press_double_long"
|
||||
TRIPPLE_BUTTON_PRESS_DOUBLE_LONG: Final = "tripple_button_press_double_long"
|
||||
MOTION_DEVICE: Final = "motion_device"
|
||||
|
||||
|
||||
|
|
|
@ -21,15 +21,21 @@ from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
|
|||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import (
|
||||
BUTTON,
|
||||
BUTTON_PRESS,
|
||||
BUTTON_PRESS_DOUBLE_LONG,
|
||||
CONF_SUBTYPE,
|
||||
DOMAIN,
|
||||
DOUBLE_BUTTON,
|
||||
DOUBLE_BUTTON_PRESS_DOUBLE_LONG,
|
||||
EVENT_CLASS,
|
||||
EVENT_CLASS_BUTTON,
|
||||
EVENT_CLASS_MOTION,
|
||||
EVENT_TYPE,
|
||||
MOTION,
|
||||
MOTION_DEVICE,
|
||||
TRIPPLE_BUTTON,
|
||||
TRIPPLE_BUTTON_PRESS_DOUBLE_LONG,
|
||||
XIAOMI_BLE_EVENT,
|
||||
)
|
||||
|
||||
|
@ -39,47 +45,47 @@ TRIGGERS_BY_TYPE = {
|
|||
MOTION_DEVICE: ["motion_detected"],
|
||||
}
|
||||
|
||||
EVENT_TYPES = {
|
||||
BUTTON: ["button"],
|
||||
DOUBLE_BUTTON: ["button_left", "button_right"],
|
||||
TRIPPLE_BUTTON: ["button_left", "button_middle", "button_right"],
|
||||
MOTION: ["motion"],
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class TriggerModelData:
|
||||
"""Data class for trigger model data."""
|
||||
|
||||
schema: vol.Schema
|
||||
event_class: str
|
||||
event_types: list[str]
|
||||
triggers: list[str]
|
||||
|
||||
|
||||
TRIGGER_MODEL_DATA = {
|
||||
BUTTON_PRESS: TriggerModelData(
|
||||
schema=DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): vol.In([EVENT_CLASS_BUTTON]),
|
||||
vol.Required(CONF_SUBTYPE): vol.In(TRIGGERS_BY_TYPE[BUTTON_PRESS]),
|
||||
}
|
||||
),
|
||||
event_class=EVENT_CLASS_BUTTON,
|
||||
event_types=EVENT_TYPES[BUTTON],
|
||||
triggers=TRIGGERS_BY_TYPE[BUTTON_PRESS],
|
||||
),
|
||||
BUTTON_PRESS_DOUBLE_LONG: TriggerModelData(
|
||||
schema=DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): vol.In([EVENT_CLASS_BUTTON]),
|
||||
vol.Required(CONF_SUBTYPE): vol.In(
|
||||
TRIGGERS_BY_TYPE[BUTTON_PRESS_DOUBLE_LONG]
|
||||
),
|
||||
}
|
||||
),
|
||||
event_class=EVENT_CLASS_BUTTON,
|
||||
event_types=EVENT_TYPES[BUTTON],
|
||||
triggers=TRIGGERS_BY_TYPE[BUTTON_PRESS_DOUBLE_LONG],
|
||||
),
|
||||
DOUBLE_BUTTON_PRESS_DOUBLE_LONG: TriggerModelData(
|
||||
event_class=EVENT_CLASS_BUTTON,
|
||||
event_types=EVENT_TYPES[DOUBLE_BUTTON],
|
||||
triggers=TRIGGERS_BY_TYPE[BUTTON_PRESS_DOUBLE_LONG],
|
||||
),
|
||||
TRIPPLE_BUTTON_PRESS_DOUBLE_LONG: TriggerModelData(
|
||||
event_class=EVENT_CLASS_BUTTON,
|
||||
event_types=EVENT_TYPES[TRIPPLE_BUTTON],
|
||||
triggers=TRIGGERS_BY_TYPE[BUTTON_PRESS_DOUBLE_LONG],
|
||||
),
|
||||
MOTION_DEVICE: TriggerModelData(
|
||||
schema=DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): vol.In([EVENT_CLASS_MOTION]),
|
||||
vol.Required(CONF_SUBTYPE): vol.In(TRIGGERS_BY_TYPE[MOTION_DEVICE]),
|
||||
}
|
||||
),
|
||||
event_class=EVENT_CLASS_MOTION,
|
||||
event_types=EVENT_TYPES[MOTION],
|
||||
triggers=TRIGGERS_BY_TYPE[MOTION_DEVICE],
|
||||
),
|
||||
}
|
||||
|
@ -90,13 +96,13 @@ MODEL_DATA = {
|
|||
"MS1BB(MI)": TRIGGER_MODEL_DATA[BUTTON_PRESS],
|
||||
"RTCGQ02LM": TRIGGER_MODEL_DATA[BUTTON_PRESS],
|
||||
"SJWS01LM": TRIGGER_MODEL_DATA[BUTTON_PRESS],
|
||||
"K9B-1BTN": TRIGGER_MODEL_DATA[BUTTON_PRESS_DOUBLE_LONG],
|
||||
"K9B-2BTN": TRIGGER_MODEL_DATA[BUTTON_PRESS_DOUBLE_LONG],
|
||||
"K9B-3BTN": TRIGGER_MODEL_DATA[BUTTON_PRESS_DOUBLE_LONG],
|
||||
"K9BB-1BTN": TRIGGER_MODEL_DATA[BUTTON_PRESS_DOUBLE_LONG],
|
||||
"YLAI003": TRIGGER_MODEL_DATA[BUTTON_PRESS_DOUBLE_LONG],
|
||||
"XMWXKG01LM": TRIGGER_MODEL_DATA[BUTTON_PRESS_DOUBLE_LONG],
|
||||
"XMWXKG01YL": TRIGGER_MODEL_DATA[BUTTON_PRESS_DOUBLE_LONG],
|
||||
"K9B-1BTN": TRIGGER_MODEL_DATA[BUTTON_PRESS_DOUBLE_LONG],
|
||||
"XMWXKG01YL": TRIGGER_MODEL_DATA[DOUBLE_BUTTON_PRESS_DOUBLE_LONG],
|
||||
"K9B-2BTN": TRIGGER_MODEL_DATA[DOUBLE_BUTTON_PRESS_DOUBLE_LONG],
|
||||
"K9B-3BTN": TRIGGER_MODEL_DATA[TRIPPLE_BUTTON_PRESS_DOUBLE_LONG],
|
||||
"MUE4094RT": TRIGGER_MODEL_DATA[MOTION_DEVICE],
|
||||
}
|
||||
|
||||
|
@ -107,7 +113,13 @@ async def async_validate_trigger_config(
|
|||
"""Validate trigger config."""
|
||||
device_id = config[CONF_DEVICE_ID]
|
||||
if model_data := _async_trigger_model_data(hass, device_id):
|
||||
return model_data.schema(config) # type: ignore[no-any-return]
|
||||
schema = DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): vol.In(model_data.event_types),
|
||||
vol.Required(CONF_SUBTYPE): vol.In(model_data.triggers),
|
||||
}
|
||||
)
|
||||
return schema(config) # type: ignore[no-any-return]
|
||||
return config
|
||||
|
||||
|
||||
|
@ -120,7 +132,7 @@ async def async_get_triggers(
|
|||
if not (model_data := _async_trigger_model_data(hass, device_id)):
|
||||
return []
|
||||
|
||||
event_type = model_data.event_class
|
||||
event_types = model_data.event_types
|
||||
event_subtypes = model_data.triggers
|
||||
return [
|
||||
{
|
||||
|
@ -132,6 +144,7 @@ async def async_get_triggers(
|
|||
CONF_TYPE: event_type,
|
||||
CONF_SUBTYPE: event_subtype,
|
||||
}
|
||||
for event_type in event_types
|
||||
for event_subtype in event_subtypes
|
||||
]
|
||||
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
},
|
||||
"trigger_type": {
|
||||
"button": "Button \"{subtype}\"",
|
||||
"button_left": "Button Left \"{subtype}\"",
|
||||
"button_middle": "Button Middle \"{subtype}\"",
|
||||
"button_right": "Button Right \"{subtype}\"",
|
||||
"motion": "{subtype}"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -164,8 +164,8 @@ async def test_get_triggers_double_button(hass: HomeAssistant) -> None:
|
|||
CONF_PLATFORM: "device",
|
||||
CONF_DOMAIN: DOMAIN,
|
||||
CONF_DEVICE_ID: device.id,
|
||||
CONF_TYPE: "button",
|
||||
CONF_SUBTYPE: "press",
|
||||
CONF_TYPE: "button_right",
|
||||
CONF_SUBTYPE: "long_press",
|
||||
"metadata": {},
|
||||
}
|
||||
triggers = await async_get_device_automations(
|
||||
|
@ -334,6 +334,66 @@ async def test_if_fires_on_button_press(hass: HomeAssistant, calls) -> None:
|
|||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_if_fires_on_double_button_long_press(hass: HomeAssistant, calls) -> None:
|
||||
"""Test for button press event trigger firing."""
|
||||
mac = "DC:ED:83:87:12:73"
|
||||
data = {"bindkey": "b93eb3787eabda352edd94b667f5d5a9"}
|
||||
entry = await _async_setup_xiaomi_device(hass, mac, data)
|
||||
|
||||
# Emit left button press event so it creates the device in the registry
|
||||
inject_bluetooth_service_info_bleak(
|
||||
hass,
|
||||
make_advertisement(
|
||||
mac,
|
||||
b"XYI\x19Ks\x12\x87\x83\xed\xdc!\xad\xb4\xcd\x02\x00\x00,\xf3\xd9\x83",
|
||||
),
|
||||
)
|
||||
|
||||
# wait for the device being created
|
||||
await hass.async_block_till_done()
|
||||
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = dev_reg.async_get_device(identifiers={get_device_id(mac)})
|
||||
device_id = device.id
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: [
|
||||
{
|
||||
"trigger": {
|
||||
CONF_PLATFORM: "device",
|
||||
CONF_DOMAIN: DOMAIN,
|
||||
CONF_DEVICE_ID: device_id,
|
||||
CONF_TYPE: "button_right",
|
||||
CONF_SUBTYPE: "press",
|
||||
},
|
||||
"action": {
|
||||
"service": "test.automation",
|
||||
"data_template": {"some": "test_trigger_right_button_press"},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
# Emit right button press event
|
||||
inject_bluetooth_service_info_bleak(
|
||||
hass,
|
||||
make_advertisement(
|
||||
mac,
|
||||
b"XYI\x19Ps\x12\x87\x83\xed\xdc\x13~~\xbe\x02\x00\x00\xf0\\;4",
|
||||
),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data["some"] == "test_trigger_right_button_press"
|
||||
|
||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_if_fires_on_motion_detected(hass: HomeAssistant, calls) -> None:
|
||||
"""Test for motion event trigger firing."""
|
||||
mac = "DE:70:E8:B2:39:0C"
|
||||
|
|
Loading…
Reference in New Issue