1 """Support for Xiaomi aqara binary sensors."""
6 BinarySensorDeviceClass,
15 from .const
import DOMAIN, GATEWAYS_KEY
16 from .entity
import XiaomiDevice
18 _LOGGER = logging.getLogger(__name__)
21 ATTR_OPEN_SINCE =
"Open since"
24 NO_MOTION =
"no_motion"
25 ATTR_LAST_ACTION =
"last_action"
26 ATTR_NO_MOTION_SINCE =
"No motion since"
29 ATTR_DENSITY =
"Density"
34 config_entry: ConfigEntry,
35 async_add_entities: AddEntitiesCallback,
37 """Perform the setup for Xiaomi devices."""
38 entities: list[XiaomiBinarySensor] = []
39 gateway = hass.data[DOMAIN][GATEWAYS_KEY][config_entry.entry_id]
40 for entity
in gateway.devices[
"binary_sensor"]:
41 model = entity[
"model"]
42 if model
in (
"motion",
"sensor_motion",
"sensor_motion.aq2"):
44 elif model
in (
"magnet",
"sensor_magnet",
"sensor_magnet.aq2"):
46 elif model ==
"sensor_wleak.aq1":
48 elif model
in (
"smoke",
"sensor_smoke"):
50 elif model
in (
"natgas",
"sensor_natgas"):
59 if "proto" not in entity
or int(entity[
"proto"][0:1]) == 1:
64 XiaomiButton(entity,
"Switch", data_key, hass, gateway, config_entry)
73 if "proto" not in entity
or int(entity[
"proto"][0:1]) == 1:
74 data_key =
"channel_0"
79 entity,
"Wall Switch", data_key, hass, gateway, config_entry
89 if "proto" not in entity
or int(entity[
"proto"][0:1]) == 1:
90 data_key_left =
"channel_0"
91 data_key_right =
"channel_1"
93 data_key_left =
"button_0"
94 data_key_right =
"button_1"
108 "Wall Switch (Right)",
118 "Wall Switch (Both)",
125 elif model
in (
"cube",
"sensor_cube",
"sensor_cube.aqgl01"):
126 entities.append(
XiaomiCube(entity, hass, gateway, config_entry))
127 elif model
in (
"vibration",
"vibration.aq1"):
132 _LOGGER.warning(
"Unmapped Device Model %s", model)
138 """Representation of a base XiaomiBinarySensor."""
140 def __init__(self, device, name, xiaomi_hub, data_key, device_class, config_entry):
141 """Initialize the XiaomiSmokeSensor."""
145 super().
__init__(device, name, xiaomi_hub, config_entry)
149 """Return true if sensor is on."""
154 """Return the class of binary sensor."""
158 """Update the sensor state."""
159 _LOGGER.debug(
"Updating xiaomi sensor (%s) by polling", self.
_sid_sid)
164 """Representation of a XiaomiNatgasSensor."""
166 def __init__(self, device, xiaomi_hub, config_entry):
167 """Initialize the XiaomiSmokeSensor."""
170 device,
"Natgas Sensor", xiaomi_hub,
"alarm",
"gas", config_entry
175 """Return the state attributes."""
177 attrs.update(super().extra_state_attributes)
181 """Handle entity which will be added."""
186 """Parse data sent by gateway."""
190 value = data.get(self.
_data_key_data_key)
194 if value
in (
"1",
"2"):
209 """Representation of a XiaomiMotionSensor."""
211 def __init__(self, device, hass, xiaomi_hub, config_entry):
212 """Initialize the XiaomiMotionSensor."""
216 if "proto" not in device
or int(device[
"proto"][0:1]) == 1:
219 data_key =
"motion_status"
221 device,
"Motion Sensor", xiaomi_hub, data_key,
"motion", config_entry
226 """Return the state attributes."""
228 attrs.update(super().extra_state_attributes)
233 """Set state to False."""
239 """Handle entity which will be added."""
244 """Parse data sent by gateway.
246 Polling (proto v1, firmware version 1.4.1_159.0143)
248 >> { "cmd":"read","sid":"158..."}
249 << {'model': 'motion', 'sid': '158...', 'short_id': 26331,
250 'cmd': 'read_ack', 'data': '{"voltage":3005}'}
252 Multicast messages (proto v1, firmware version 1.4.1_159.0143)
254 << {'model': 'motion', 'sid': '158...', 'short_id': 26331,
255 'cmd': 'report', 'data': '{"status":"motion"}'}
256 << {'model': 'motion', 'sid': '158...', 'short_id': 26331,
257 'cmd': 'report', 'data': '{"no_motion":"120"}'}
258 << {'model': 'motion', 'sid': '158...', 'short_id': 26331,
259 'cmd': 'report', 'data': '{"no_motion":"180"}'}
260 << {'model': 'motion', 'sid': '158...', 'short_id': 26331,
261 'cmd': 'report', 'data': '{"no_motion":"300"}'}
262 << {'model': 'motion', 'sid': '158...', 'short_id': 26331,
263 'cmd': 'heartbeat', 'data': '{"voltage":3005}'}
266 if raw_data[
"cmd"] ==
"heartbeat":
268 "Skipping heartbeat of the motion sensor. "
269 "It can introduce an incorrect state because of a firmware "
270 "bug (https://github.com/home-assistant/core/pull/"
271 "11631#issuecomment-357507744)"
275 if NO_MOTION
in data:
293 self.
_hass_hass.bus.async_fire(
294 "xiaomi_aqara.motion", {
"entity_id": self.
entity_identity_id}
307 """Representation of a XiaomiDoorSensor."""
309 def __init__(self, device, xiaomi_hub, config_entry):
310 """Initialize the XiaomiDoorSensor."""
312 if "proto" not in device
or int(device[
"proto"][0:1]) == 1:
315 data_key =
"window_status"
318 "Door Window Sensor",
321 BinarySensorDeviceClass.OPENING,
327 """Return the state attributes."""
328 attrs = {ATTR_OPEN_SINCE: self.
_open_since_open_since}
329 attrs.update(super().extra_state_attributes)
333 """Handle entity which will be added."""
341 """Parse data sent by gateway."""
347 value = data.get(self.
_data_key_data_key)
368 """Representation of a XiaomiWaterLeakSensor."""
370 def __init__(self, device, xiaomi_hub, config_entry):
371 """Initialize the XiaomiWaterLeakSensor."""
372 if "proto" not in device
or int(device[
"proto"][0:1]) == 1:
375 data_key =
"wleak_status"
381 BinarySensorDeviceClass.MOISTURE,
386 """Handle entity which will be added."""
391 """Parse data sent by gateway."""
394 value = data.get(self.
_data_key_data_key)
404 if value ==
"no_leak":
414 """Representation of a XiaomiSmokeSensor."""
416 def __init__(self, device, xiaomi_hub, config_entry):
417 """Initialize the XiaomiSmokeSensor."""
420 device,
"Smoke Sensor", xiaomi_hub,
"alarm",
"smoke", config_entry
425 """Return the state attributes."""
427 attrs.update(super().extra_state_attributes)
431 """Handle entity which will be added."""
436 """Parse data sent by gateway."""
439 value = data.get(self.
_data_key_data_key)
443 if value
in (
"1",
"2"):
458 """Representation of a Xiaomi Vibration Sensor."""
460 def __init__(self, device, name, data_key, xiaomi_hub, config_entry):
461 """Initialize the XiaomiVibration."""
463 super().
__init__(device, name, xiaomi_hub, data_key,
None, config_entry)
467 """Return the state attributes."""
468 attrs = {ATTR_LAST_ACTION: self.
_last_action_last_action}
469 attrs.update(super().extra_state_attributes)
473 """Handle entity which will be added."""
478 """Parse data sent by gateway."""
479 value = data.get(self.
_data_key_data_key)
483 if value
not in (
"vibrate",
"tilt",
"free_fall",
"actively"):
484 _LOGGER.warning(
"Unsupported movement_type detected: %s", value)
487 self.
hasshass.bus.async_fire(
488 "xiaomi_aqara.movement",
489 {
"entity_id": self.
entity_identity_id,
"movement_type": value},
497 """Representation of a Xiaomi Button."""
499 def __init__(self, device, name, data_key, hass, xiaomi_hub, config_entry):
500 """Initialize the XiaomiButton."""
503 super().
__init__(device, name, xiaomi_hub, data_key,
None, config_entry)
507 """Return the state attributes."""
508 attrs = {ATTR_LAST_ACTION: self.
_last_action_last_action}
509 attrs.update(super().extra_state_attributes)
513 """Handle entity which will be added."""
518 """Parse data sent by gateway."""
519 value = data.get(self.
_data_key_data_key)
523 if value ==
"long_click_press":
525 click_type =
"long_click_press"
526 elif value ==
"long_click_release":
529 elif value ==
"click":
530 click_type =
"single"
531 elif value ==
"double_click":
532 click_type =
"double"
533 elif value ==
"both_click":
535 elif value ==
"double_both_click":
536 click_type =
"double_both"
537 elif value ==
"shake":
539 elif value ==
"long_click":
541 elif value ==
"long_both_click":
542 click_type =
"long_both"
544 _LOGGER.warning(
"Unsupported click_type detected: %s", value)
547 self.
_hass_hass.bus.async_fire(
548 "xiaomi_aqara.click",
549 {
"entity_id": self.
entity_identity_id,
"click_type": click_type},
557 """Representation of a Xiaomi Cube."""
559 def __init__(self, device, hass, xiaomi_hub, config_entry):
560 """Initialize the Xiaomi Cube."""
563 if "proto" not in device
or int(device[
"proto"][0:1]) == 1:
566 data_key =
"cube_status"
567 super().
__init__(device,
"Cube", xiaomi_hub, data_key,
None, config_entry)
571 """Return the state attributes."""
572 attrs = {ATTR_LAST_ACTION: self.
_last_action_last_action}
573 attrs.update(super().extra_state_attributes)
577 """Handle entity which will be added."""
582 """Parse data sent by gateway."""
584 self.
_hass_hass.bus.async_fire(
585 "xiaomi_aqara.cube_action",
591 action_value =
float(
593 if isinstance(data[
"rotate"], int)
594 else data[
"rotate"].replace(
",",
".")
596 self.
_hass_hass.bus.async_fire(
597 "xiaomi_aqara.cube_action",
600 "action_type":
"rotate",
601 "action_value": action_value,
606 if "rotate_degree" in data:
607 action_value =
float(
608 data[
"rotate_degree"]
609 if isinstance(data[
"rotate_degree"], int)
610 else data[
"rotate_degree"].replace(
",",
".")
612 self.
_hass_hass.bus.async_fire(
613 "xiaomi_aqara.cube_action",
616 "action_type":
"rotate",
617 "action_value": action_value,
def __init__(self, device, name, xiaomi_hub, data_key, device_class, config_entry)
def extra_state_attributes(self)
def __init__(self, device, hass, xiaomi_hub, config_entry)
None async_added_to_hass(self)
def parse_data(self, data, raw_data)
def __init__(self, device, xiaomi_hub, config_entry)
None async_added_to_hass(self)
def extra_state_attributes(self)
def parse_data(self, data, raw_data)
def __init__(self, device, hass, xiaomi_hub, config_entry)
None async_added_to_hass(self)
def extra_state_attributes(self)
def _async_set_no_motion(self, now)
def parse_data(self, data, raw_data)
def extra_state_attributes(self)
None async_added_to_hass(self)
def parse_data(self, data, raw_data)
def __init__(self, device, xiaomi_hub, config_entry)
None async_added_to_hass(self)
def __init__(self, device, xiaomi_hub, config_entry)
def parse_data(self, data, raw_data)
def extra_state_attributes(self)
def extra_state_attributes(self)
None async_added_to_hass(self)
def __init__(self, device, name, data_key, xiaomi_hub, config_entry)
def parse_data(self, data, raw_data)
None async_added_to_hass(self)
def __init__(self, device, xiaomi_hub, config_entry)
def parse_data(self, data, raw_data)
None async_write_ha_state(self)
State|None async_get_last_state(self)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
CALLBACK_TYPE async_call_later(HomeAssistant hass, float|timedelta delay, HassJob[[datetime], Coroutine[Any, Any, None]|None]|Callable[[datetime], Coroutine[Any, Any, None]|None] action)