mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Fix Hue events for relative_rotary devices (such as Hue Tap Dial) (#76758)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
453cbc3e14
commit
223ea03492
@ -28,6 +28,8 @@ TRIGGER_SUBTYPE = {
|
||||
"2": "second button",
|
||||
"3": "third button",
|
||||
"4": "fourth button",
|
||||
"clock_wise": "Rotation clockwise",
|
||||
"counter_clock_wise": "Rotation counter-clockwise",
|
||||
}
|
||||
TRIGGER_TYPE = {
|
||||
"remote_button_long_release": "{subtype} released after long press",
|
||||
@ -40,6 +42,7 @@ TRIGGER_TYPE = {
|
||||
"short_release": "{subtype} released after short press",
|
||||
"long_release": "{subtype} released after long press",
|
||||
"double_short_release": "both {subtype} released",
|
||||
"start": '"{subtype}" pressed initially',
|
||||
}
|
||||
|
||||
UNKNOWN_TYPE = "unknown type"
|
||||
|
@ -49,20 +49,23 @@
|
||||
"1": "First button",
|
||||
"2": "Second button",
|
||||
"3": "Third button",
|
||||
"4": "Fourth button"
|
||||
"4": "Fourth button",
|
||||
"clock_wise": "Rotation clockwise",
|
||||
"counter_clock_wise": "Rotation counter-clockwise"
|
||||
},
|
||||
"trigger_type": {
|
||||
"remote_button_long_release": "\"{subtype}\" button released after long press",
|
||||
"remote_button_short_press": "\"{subtype}\" button pressed",
|
||||
"remote_button_short_release": "\"{subtype}\" button released",
|
||||
"remote_button_long_release": "\"{subtype}\" released after long press",
|
||||
"remote_button_short_press": "\"{subtype}\" pressed",
|
||||
"remote_button_short_release": "\"{subtype}\" released",
|
||||
"remote_double_button_long_press": "Both \"{subtype}\" released after long press",
|
||||
"remote_double_button_short_press": "Both \"{subtype}\" released",
|
||||
|
||||
"initial_press": "Button \"{subtype}\" pressed initially",
|
||||
"repeat": "Button \"{subtype}\" held down",
|
||||
"short_release": "Button \"{subtype}\" released after short press",
|
||||
"long_release": "Button \"{subtype}\" released after long press",
|
||||
"double_short_release": "Both \"{subtype}\" released"
|
||||
"initial_press": "\"{subtype}\" pressed initially",
|
||||
"repeat": "\"{subtype}\" held down",
|
||||
"short_release": "\"{subtype}\" released after short press",
|
||||
"long_release": "\"{subtype}\" released after long press",
|
||||
"double_short_release": "Both \"{subtype}\" released",
|
||||
"start": "\"{subtype}\" pressed initially"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -49,19 +49,22 @@
|
||||
"double_buttons_1_3": "First and Third buttons",
|
||||
"double_buttons_2_4": "Second and Fourth buttons",
|
||||
"turn_off": "Turn off",
|
||||
"turn_on": "Turn on"
|
||||
"turn_on": "Turn on",
|
||||
"clock_wise": "Rotation clockwise",
|
||||
"counter_clock_wise": "Rotation counter-clockwise"
|
||||
},
|
||||
"trigger_type": {
|
||||
"double_short_release": "Both \"{subtype}\" released",
|
||||
"initial_press": "Button \"{subtype}\" pressed initially",
|
||||
"long_release": "Button \"{subtype}\" released after long press",
|
||||
"remote_button_long_release": "\"{subtype}\" button released after long press",
|
||||
"remote_button_short_press": "\"{subtype}\" button pressed",
|
||||
"remote_button_short_release": "\"{subtype}\" button released",
|
||||
"initial_press": "\"{subtype}\" pressed initially",
|
||||
"long_release": "\"{subtype}\" released after long press",
|
||||
"remote_button_long_release": "\"{subtype}\" released after long press",
|
||||
"remote_button_short_press": "\"{subtype}\" pressed",
|
||||
"remote_button_short_release": "\"{subtype}\" released",
|
||||
"remote_double_button_long_press": "Both \"{subtype}\" released after long press",
|
||||
"remote_double_button_short_press": "Both \"{subtype}\" released",
|
||||
"repeat": "Button \"{subtype}\" held down",
|
||||
"short_release": "Button \"{subtype}\" released after short press"
|
||||
"repeat": "\"{subtype}\" held down",
|
||||
"short_release": "\"{subtype}\" released after short press",
|
||||
"start": "\"{subtype}\" pressed initially"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -4,10 +4,13 @@ from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from aiohue.v2.models.button import ButtonEvent
|
||||
from aiohue.v2.models.relative_rotary import (
|
||||
RelativeRotaryAction,
|
||||
RelativeRotaryDirection,
|
||||
)
|
||||
from aiohue.v2.models.resource import ResourceTypes
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
|
||||
from homeassistant.components.homeassistant.triggers import event as event_trigger
|
||||
from homeassistant.const import (
|
||||
@ -49,48 +52,25 @@ DEFAULT_BUTTON_EVENT_TYPES = (
|
||||
ButtonEvent.LONG_RELEASE,
|
||||
)
|
||||
|
||||
DEFAULT_ROTARY_EVENT_TYPES = (RelativeRotaryAction.START, RelativeRotaryAction.REPEAT)
|
||||
DEFAULT_ROTARY_EVENT_SUBTYPES = (
|
||||
RelativeRotaryDirection.CLOCK_WISE,
|
||||
RelativeRotaryDirection.COUNTER_CLOCK_WISE,
|
||||
)
|
||||
|
||||
DEVICE_SPECIFIC_EVENT_TYPES = {
|
||||
# device specific overrides of specific supported button events
|
||||
"Hue tap switch": (ButtonEvent.INITIAL_PRESS,),
|
||||
}
|
||||
|
||||
|
||||
def check_invalid_device_trigger(
|
||||
bridge: HueBridge,
|
||||
config: ConfigType,
|
||||
device_entry: DeviceEntry,
|
||||
automation_info: AutomationTriggerInfo | None = None,
|
||||
):
|
||||
"""Check automation config for deprecated format."""
|
||||
# NOTE: Remove this check after 2022.6
|
||||
if isinstance(config["subtype"], int):
|
||||
return
|
||||
# found deprecated V1 style trigger, notify the user that it should be adjusted
|
||||
msg = (
|
||||
f"Incompatible device trigger detected for "
|
||||
f"[{device_entry.name}](/config/devices/device/{device_entry.id}) "
|
||||
"Please manually fix the outdated automation(s) once to fix this issue."
|
||||
)
|
||||
if automation_info:
|
||||
automation_id = automation_info["variables"]["this"]["attributes"]["id"] # type: ignore[index]
|
||||
msg += f"\n\n[Check it out](/config/automation/edit/{automation_id})."
|
||||
persistent_notification.async_create(
|
||||
bridge.hass,
|
||||
msg,
|
||||
title="Outdated device trigger found",
|
||||
notification_id=f"hue_trigger_{device_entry.id}",
|
||||
)
|
||||
|
||||
|
||||
async def async_validate_trigger_config(
|
||||
bridge: "HueBridge",
|
||||
device_entry: DeviceEntry,
|
||||
config: ConfigType,
|
||||
) -> ConfigType:
|
||||
"""Validate config."""
|
||||
config = TRIGGER_SCHEMA(config)
|
||||
check_invalid_device_trigger(bridge, config, device_entry)
|
||||
return config
|
||||
return TRIGGER_SCHEMA(config)
|
||||
|
||||
|
||||
async def async_attach_trigger(
|
||||
@ -113,7 +93,6 @@ async def async_attach_trigger(
|
||||
},
|
||||
}
|
||||
)
|
||||
check_invalid_device_trigger(bridge, config, device_entry, automation_info)
|
||||
return await event_trigger.async_attach_trigger(
|
||||
hass, event_config, action, automation_info, platform_type="device"
|
||||
)
|
||||
@ -131,9 +110,10 @@ def async_get_triggers(
|
||||
# extract triggers from all button resources of this Hue device
|
||||
triggers = []
|
||||
model_id = api.devices[hue_dev_id].product_data.product_name
|
||||
|
||||
for resource in api.devices.get_sensors(hue_dev_id):
|
||||
if resource.type != ResourceTypes.BUTTON:
|
||||
continue
|
||||
# button triggers
|
||||
if resource.type == ResourceTypes.BUTTON:
|
||||
for event_type in DEVICE_SPECIFIC_EVENT_TYPES.get(
|
||||
model_id, DEFAULT_BUTTON_EVENT_TYPES
|
||||
):
|
||||
@ -147,6 +127,20 @@ def async_get_triggers(
|
||||
CONF_UNIQUE_ID: resource.id,
|
||||
}
|
||||
)
|
||||
# relative_rotary triggers
|
||||
elif resource.type == ResourceTypes.RELATIVE_ROTARY:
|
||||
for event_type in DEFAULT_ROTARY_EVENT_TYPES:
|
||||
for sub_type in DEFAULT_ROTARY_EVENT_SUBTYPES:
|
||||
triggers.append(
|
||||
{
|
||||
CONF_DEVICE_ID: device_entry.id,
|
||||
CONF_DOMAIN: DOMAIN,
|
||||
CONF_PLATFORM: "device",
|
||||
CONF_TYPE: event_type.value,
|
||||
CONF_SUBTYPE: sub_type.value,
|
||||
CONF_UNIQUE_ID: resource.id,
|
||||
}
|
||||
)
|
||||
return triggers
|
||||
|
||||
|
||||
|
@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
|
||||
from aiohue.v2 import HueBridgeV2
|
||||
from aiohue.v2.controllers.events import EventType
|
||||
from aiohue.v2.models.button import Button
|
||||
from aiohue.v2.models.relative_rotary import RelativeRotary
|
||||
|
||||
from homeassistant.const import CONF_DEVICE_ID, CONF_ID, CONF_TYPE, CONF_UNIQUE_ID
|
||||
from homeassistant.core import callback
|
||||
@ -14,6 +15,8 @@ from homeassistant.util import slugify
|
||||
from ..const import ATTR_HUE_EVENT, CONF_SUBTYPE, DOMAIN as DOMAIN
|
||||
|
||||
CONF_CONTROL_ID = "control_id"
|
||||
CONF_DURATION = "duration"
|
||||
CONF_STEPS = "steps"
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..bridge import HueBridge
|
||||
@ -28,12 +31,12 @@ async def async_setup_hue_events(bridge: "HueBridge"):
|
||||
conf_entry = bridge.config_entry
|
||||
dev_reg = device_registry.async_get(hass)
|
||||
|
||||
# at this time the `button` resource is the only source of hue events
|
||||
btn_controller = api.sensors.button
|
||||
rotary_controller = api.sensors.relative_rotary
|
||||
|
||||
@callback
|
||||
def handle_button_event(evt_type: EventType, hue_resource: Button) -> None:
|
||||
"""Handle event from Hue devices controller."""
|
||||
"""Handle event from Hue button resource controller."""
|
||||
LOGGER.debug("Received button event: %s", hue_resource)
|
||||
|
||||
# guard for missing button object on the resource
|
||||
@ -60,3 +63,29 @@ async def async_setup_hue_events(bridge: "HueBridge"):
|
||||
handle_button_event, event_filter=EventType.RESOURCE_UPDATED
|
||||
)
|
||||
)
|
||||
|
||||
@callback
|
||||
def handle_rotary_event(evt_type: EventType, hue_resource: RelativeRotary) -> None:
|
||||
"""Handle event from Hue relative_rotary resource controller."""
|
||||
LOGGER.debug("Received relative_rotary event: %s", hue_resource)
|
||||
|
||||
hue_device = btn_controller.get_device(hue_resource.id)
|
||||
device = dev_reg.async_get_device({(DOMAIN, hue_device.id)})
|
||||
|
||||
# Fire event
|
||||
data = {
|
||||
CONF_DEVICE_ID: device.id, # type: ignore[union-attr]
|
||||
CONF_UNIQUE_ID: hue_resource.id,
|
||||
CONF_TYPE: hue_resource.relative_rotary.last_event.action.value,
|
||||
CONF_SUBTYPE: hue_resource.relative_rotary.last_event.rotation.direction.value,
|
||||
CONF_DURATION: hue_resource.relative_rotary.last_event.rotation.duration,
|
||||
CONF_STEPS: hue_resource.relative_rotary.last_event.rotation.steps,
|
||||
}
|
||||
hass.bus.async_fire(ATTR_HUE_EVENT, data)
|
||||
|
||||
# add listener for updates from `relative_rotary` resource
|
||||
conf_entry.async_on_unload(
|
||||
rotary_controller.subscribe(
|
||||
handle_rotary_event, event_filter=EventType.RESOURCE_UPDATED
|
||||
)
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user