"""Representation of a deCONZ remote or keypad.""" from pydeconz.sensor import ( ANCILLARY_CONTROL_ARMED_AWAY, ANCILLARY_CONTROL_ARMED_NIGHT, ANCILLARY_CONTROL_ARMED_STAY, ANCILLARY_CONTROL_DISARMED, AncillaryControl, Switch, ) from homeassistant.const import ( CONF_CODE, CONF_DEVICE_ID, CONF_EVENT, CONF_ID, CONF_UNIQUE_ID, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, STATE_UNKNOWN, ) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util import slugify from .const import CONF_ANGLE, CONF_GESTURE, CONF_XY, LOGGER, NEW_SENSOR from .deconz_device import DeconzBase CONF_DECONZ_EVENT = "deconz_event" CONF_DECONZ_ALARM_EVENT = "deconz_alarm_event" DECONZ_TO_ALARM_STATE = { ANCILLARY_CONTROL_ARMED_AWAY: STATE_ALARM_ARMED_AWAY, ANCILLARY_CONTROL_ARMED_NIGHT: STATE_ALARM_ARMED_NIGHT, ANCILLARY_CONTROL_ARMED_STAY: STATE_ALARM_ARMED_HOME, ANCILLARY_CONTROL_DISARMED: STATE_ALARM_DISARMED, } async def async_setup_events(gateway) -> None: """Set up the deCONZ events.""" @callback def async_add_sensor(sensors=gateway.api.sensors.values()): """Create DeconzEvent.""" for sensor in sensors: if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"): continue if ( sensor.type not in Switch.ZHATYPE + AncillaryControl.ZHATYPE or sensor.uniqueid in {event.unique_id for event in gateway.events} ): continue if sensor.type in Switch.ZHATYPE: new_event = DeconzEvent(sensor, gateway) elif sensor.type in AncillaryControl.ZHATYPE: new_event = DeconzAlarmEvent(sensor, gateway) gateway.hass.async_create_task(new_event.async_update_device_registry()) gateway.events.append(new_event) gateway.config_entry.async_on_unload( async_dispatcher_connect( gateway.hass, gateway.async_signal_new_device(NEW_SENSOR), async_add_sensor ) ) async_add_sensor() @callback def async_unload_events(gateway) -> None: """Unload all deCONZ events.""" for event in gateway.events: event.async_will_remove_from_hass() gateway.events.clear() class DeconzEvent(DeconzBase): """When you want signals instead of entities. Stateless sensors such as remotes are expected to generate an event instead of a sensor entity in hass. """ def __init__(self, device, gateway): """Register callback that will be used for signals.""" super().__init__(device, gateway) self._device.register_callback(self.async_update_callback) self.device_id = None self.event_id = slugify(self._device.name) LOGGER.debug("deCONZ event created: %s", self.event_id) @property def device(self): """Return Event device.""" return self._device @callback def async_will_remove_from_hass(self) -> None: """Disconnect event object when removed.""" self._device.remove_callback(self.async_update_callback) @callback def async_update_callback(self, force_update=False): """Fire the event if reason is that state is updated.""" if ( self.gateway.ignore_state_updates or "state" not in self._device.changed_keys ): return data = { CONF_ID: self.event_id, CONF_UNIQUE_ID: self.serial, CONF_EVENT: self._device.state, } if self.device_id: data[CONF_DEVICE_ID] = self.device_id if self._device.gesture is not None: data[CONF_GESTURE] = self._device.gesture if self._device.angle is not None: data[CONF_ANGLE] = self._device.angle if self._device.xy is not None: data[CONF_XY] = self._device.xy self.gateway.hass.bus.async_fire(CONF_DECONZ_EVENT, data) async def async_update_device_registry(self) -> None: """Update device registry.""" if not self.device_info: return device_registry = ( await self.gateway.hass.helpers.device_registry.async_get_registry() ) entry = device_registry.async_get_or_create( config_entry_id=self.gateway.config_entry.entry_id, **self.device_info ) self.device_id = entry.id class DeconzAlarmEvent(DeconzEvent): """Alarm control panel companion event when user inputs a code.""" @callback def async_update_callback(self, force_update=False): """Fire the event if reason is that state is updated.""" if ( self.gateway.ignore_state_updates or "action" not in self._device.changed_keys or self._device.action == "" ): return state, code, _area = self._device.action.split(",") data = { CONF_ID: self.event_id, CONF_UNIQUE_ID: self.serial, CONF_DEVICE_ID: self.device_id, CONF_EVENT: DECONZ_TO_ALARM_STATE.get(state, STATE_UNKNOWN), CONF_CODE: code, } self.gateway.hass.bus.async_fire(CONF_DECONZ_ALARM_EVENT, data)