mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Add deconz_relative_rotary event for Hue Tap Dial (#82727)
This commit is contained in:
parent
4928c3d683
commit
3853182ccf
@ -57,3 +57,6 @@ POWER_PLUGS = [
|
||||
|
||||
CONF_ANGLE = "angle"
|
||||
CONF_GESTURE = "gesture"
|
||||
|
||||
ATTR_DURATION = "duration"
|
||||
ATTR_ROTATION = "rotation"
|
||||
|
@ -10,6 +10,7 @@ from pydeconz.models.sensor.ancillary_control import (
|
||||
AncillaryControlAction,
|
||||
)
|
||||
from pydeconz.models.sensor.presence import Presence, PresenceStatePresenceEvent
|
||||
from pydeconz.models.sensor.relative_rotary import RelativeRotary, RelativeRotaryEvent
|
||||
from pydeconz.models.sensor.switch import Switch
|
||||
|
||||
from homeassistant.const import (
|
||||
@ -23,13 +24,14 @@ from homeassistant.core import callback
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.util import slugify
|
||||
|
||||
from .const import CONF_ANGLE, CONF_GESTURE, LOGGER
|
||||
from .const import ATTR_DURATION, ATTR_ROTATION, CONF_ANGLE, CONF_GESTURE, LOGGER
|
||||
from .deconz_device import DeconzBase
|
||||
from .gateway import DeconzGateway
|
||||
|
||||
CONF_DECONZ_EVENT = "deconz_event"
|
||||
CONF_DECONZ_ALARM_EVENT = "deconz_alarm_event"
|
||||
CONF_DECONZ_PRESENCE_EVENT = "deconz_presence_event"
|
||||
CONF_DECONZ_RELATIVE_ROTARY_EVENT = "deconz_relative_rotary_event"
|
||||
|
||||
SUPPORTED_DECONZ_ALARM_EVENTS = {
|
||||
AncillaryControlAction.EMERGENCY,
|
||||
@ -47,6 +49,10 @@ SUPPORTED_DECONZ_PRESENCE_EVENTS = {
|
||||
PresenceStatePresenceEvent.APPROACHING,
|
||||
PresenceStatePresenceEvent.ABSENTING,
|
||||
}
|
||||
RELATIVE_ROTARY_DECONZ_TO_EVENT = {
|
||||
RelativeRotaryEvent.NEW: "new",
|
||||
RelativeRotaryEvent.REPEAT: "repeat",
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_events(gateway: DeconzGateway) -> None:
|
||||
@ -55,7 +61,7 @@ async def async_setup_events(gateway: DeconzGateway) -> None:
|
||||
@callback
|
||||
def async_add_sensor(_: EventType, sensor_id: str) -> None:
|
||||
"""Create DeconzEvent."""
|
||||
new_event: DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent
|
||||
new_event: DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent | DeconzRelativeRotaryEvent
|
||||
sensor = gateway.api.sensors[sensor_id]
|
||||
|
||||
if isinstance(sensor, Switch):
|
||||
@ -69,6 +75,9 @@ async def async_setup_events(gateway: DeconzGateway) -> None:
|
||||
return
|
||||
new_event = DeconzPresenceEvent(sensor, gateway)
|
||||
|
||||
elif isinstance(sensor, RelativeRotary):
|
||||
new_event = DeconzRelativeRotaryEvent(sensor, gateway)
|
||||
|
||||
gateway.hass.async_create_task(new_event.async_update_device_registry())
|
||||
gateway.events.append(new_event)
|
||||
|
||||
@ -84,6 +93,10 @@ async def async_setup_events(gateway: DeconzGateway) -> None:
|
||||
async_add_sensor,
|
||||
gateway.api.sensors.presence,
|
||||
)
|
||||
gateway.register_platform_add_device_callback(
|
||||
async_add_sensor,
|
||||
gateway.api.sensors.relative_rotary,
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
@ -104,7 +117,7 @@ class DeconzEventBase(DeconzBase):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
device: AncillaryControl | Presence | Switch,
|
||||
device: AncillaryControl | Presence | RelativeRotary | Switch,
|
||||
gateway: DeconzGateway,
|
||||
) -> None:
|
||||
"""Register callback that will be used for signals."""
|
||||
@ -227,3 +240,29 @@ class DeconzPresenceEvent(DeconzEventBase):
|
||||
}
|
||||
|
||||
self.gateway.hass.bus.async_fire(CONF_DECONZ_PRESENCE_EVENT, data)
|
||||
|
||||
|
||||
class DeconzRelativeRotaryEvent(DeconzEventBase):
|
||||
"""Relative rotary event."""
|
||||
|
||||
_device: RelativeRotary
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Fire the event if reason is new action is updated."""
|
||||
if (
|
||||
self.gateway.ignore_state_updates
|
||||
or "rotaryevent" not in self._device.changed_keys
|
||||
):
|
||||
return
|
||||
|
||||
data = {
|
||||
CONF_ID: self.event_id,
|
||||
CONF_UNIQUE_ID: self.serial,
|
||||
CONF_DEVICE_ID: self.device_id,
|
||||
CONF_EVENT: RELATIVE_ROTARY_DECONZ_TO_EVENT[self._device.rotary_event],
|
||||
ATTR_ROTATION: self._device.expected_rotation,
|
||||
ATTR_DURATION: self._device.expected_event_duration,
|
||||
}
|
||||
|
||||
self.gateway.hass.bus.async_fire(CONF_DECONZ_RELATIVE_ROTARY_EVENT, data)
|
||||
|
@ -28,6 +28,7 @@ from .deconz_event import (
|
||||
DeconzAlarmEvent,
|
||||
DeconzEvent,
|
||||
DeconzPresenceEvent,
|
||||
DeconzRelativeRotaryEvent,
|
||||
)
|
||||
from .gateway import DeconzGateway
|
||||
|
||||
@ -635,7 +636,7 @@ TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
def _get_deconz_event_from_device(
|
||||
hass: HomeAssistant,
|
||||
device: dr.DeviceEntry,
|
||||
) -> DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent:
|
||||
) -> DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent | DeconzRelativeRotaryEvent:
|
||||
"""Resolve deconz event from device."""
|
||||
gateways: dict[str, DeconzGateway] = hass.data.get(DOMAIN, {})
|
||||
for gateway in gateways.values():
|
||||
|
@ -41,7 +41,12 @@ from .const import (
|
||||
from .errors import AuthenticationRequired, CannotConnect
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .deconz_event import DeconzAlarmEvent, DeconzEvent, DeconzPresenceEvent
|
||||
from .deconz_event import (
|
||||
DeconzAlarmEvent,
|
||||
DeconzEvent,
|
||||
DeconzPresenceEvent,
|
||||
DeconzRelativeRotaryEvent,
|
||||
)
|
||||
|
||||
SENSORS = (
|
||||
sensors.SensorResourceManager,
|
||||
@ -93,7 +98,12 @@ class DeconzGateway:
|
||||
|
||||
self.deconz_ids: dict[str, str] = {}
|
||||
self.entities: dict[str, set[str]] = {}
|
||||
self.events: list[DeconzAlarmEvent | DeconzEvent | DeconzPresenceEvent] = []
|
||||
self.events: list[
|
||||
DeconzAlarmEvent
|
||||
| DeconzEvent
|
||||
| DeconzPresenceEvent
|
||||
| DeconzRelativeRotaryEvent
|
||||
] = []
|
||||
self.clip_sensors: set[tuple[Callable[[EventType, str], None], str]] = set()
|
||||
self.deconz_groups: set[tuple[Callable[[EventType, str], None], str]] = set()
|
||||
self.ignored_devices: set[tuple[Callable[[EventType, str], None], str]] = set()
|
||||
|
@ -10,9 +10,13 @@ from pydeconz.models.sensor.presence import PresenceStatePresenceEvent
|
||||
|
||||
from homeassistant.components.deconz.const import DOMAIN as DECONZ_DOMAIN
|
||||
from homeassistant.components.deconz.deconz_event import (
|
||||
ATTR_DURATION,
|
||||
ATTR_ROTATION,
|
||||
CONF_DECONZ_ALARM_EVENT,
|
||||
CONF_DECONZ_EVENT,
|
||||
CONF_DECONZ_PRESENCE_EVENT,
|
||||
CONF_DECONZ_RELATIVE_ROTARY_EVENT,
|
||||
RELATIVE_ROTARY_DECONZ_TO_EVENT,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_ID,
|
||||
@ -515,6 +519,105 @@ async def test_deconz_presence_events(hass, aioclient_mock, mock_deconz_websocke
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_deconz_relative_rotary_events(
|
||||
hass, aioclient_mock, mock_deconz_websocket
|
||||
):
|
||||
"""Test successful creation of deconz relative rotary events."""
|
||||
data = {
|
||||
"sensors": {
|
||||
"1": {
|
||||
"config": {
|
||||
"battery": 100,
|
||||
"on": True,
|
||||
"reachable": True,
|
||||
},
|
||||
"etag": "463728970bdb7d04048fc4373654f45a",
|
||||
"lastannounced": "2022-07-03T13:57:59Z",
|
||||
"lastseen": "2022-07-03T14:02Z",
|
||||
"manufacturername": "Signify Netherlands B.V.",
|
||||
"modelid": "RDM002",
|
||||
"name": "RDM002 44",
|
||||
"state": {
|
||||
"expectedeventduration": 400,
|
||||
"expectedrotation": 75,
|
||||
"lastupdated": "2022-07-03T11:37:49.586",
|
||||
"rotaryevent": 2,
|
||||
},
|
||||
"swversion": "2.59.19",
|
||||
"type": "ZHARelativeRotary",
|
||||
"uniqueid": "xx:xx:xx:xx:xx:xx:xx:xx-14-fc00",
|
||||
}
|
||||
}
|
||||
}
|
||||
with patch.dict(DECONZ_WEB_REQUEST, data):
|
||||
config_entry = await setup_deconz_integration(hass, aioclient_mock)
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
|
||||
assert len(hass.states.async_all()) == 1
|
||||
assert (
|
||||
len(dr.async_entries_for_config_entry(device_registry, config_entry.entry_id))
|
||||
== 3
|
||||
)
|
||||
|
||||
device = device_registry.async_get_device(
|
||||
identifiers={(DECONZ_DOMAIN, "xx:xx:xx:xx:xx:xx:xx:xx")}
|
||||
)
|
||||
|
||||
captured_events = async_capture_events(hass, CONF_DECONZ_RELATIVE_ROTARY_EVENT)
|
||||
|
||||
for rotary_event, duration, rotation in ((1, 100, 50), (2, 200, -50)):
|
||||
event_changed_sensor = {
|
||||
"t": "event",
|
||||
"e": "changed",
|
||||
"r": "sensors",
|
||||
"id": "1",
|
||||
"state": {
|
||||
"rotaryevent": rotary_event,
|
||||
"expectedeventduration": duration,
|
||||
"expectedrotation": rotation,
|
||||
},
|
||||
}
|
||||
await mock_deconz_websocket(data=event_changed_sensor)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(captured_events) == 1
|
||||
assert captured_events[0].data == {
|
||||
CONF_ID: "rdm002_44",
|
||||
CONF_UNIQUE_ID: "xx:xx:xx:xx:xx:xx:xx:xx",
|
||||
CONF_DEVICE_ID: device.id,
|
||||
CONF_EVENT: RELATIVE_ROTARY_DECONZ_TO_EVENT[rotary_event],
|
||||
ATTR_DURATION: duration,
|
||||
ATTR_ROTATION: rotation,
|
||||
}
|
||||
captured_events.clear()
|
||||
|
||||
# Unsupported relative rotary event
|
||||
|
||||
event_changed_sensor = {
|
||||
"t": "event",
|
||||
"e": "changed",
|
||||
"r": "sensors",
|
||||
"id": "1",
|
||||
"name": "123",
|
||||
}
|
||||
await mock_deconz_websocket(data=event_changed_sensor)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(captured_events) == 0
|
||||
|
||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
|
||||
states = hass.states.async_all()
|
||||
assert len(hass.states.async_all()) == 1
|
||||
for state in states:
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
await hass.config_entries.async_remove(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_deconz_events_bad_unique_id(hass, aioclient_mock):
|
||||
"""Verify no devices are created if unique id is bad or missing."""
|
||||
data = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user