1
mirror of https://github.com/home-assistant/core synced 2024-08-02 23:40:32 +02:00
ha-core/homeassistant/components/xiaomi_miio/binary_sensor.py
epenet cb135bc889
Add basic type hints to xiaomi_miio (#62889)
Co-authored-by: epenet <epenet@users.noreply.github.com>
2021-12-28 00:15:40 +01:00

238 lines
8.0 KiB
Python

"""Support for Xiaomi Miio binary sensors."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
import logging
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import VacuumCoordinatorDataAttributes
from .const import (
CONF_DEVICE,
CONF_FLOW_TYPE,
CONF_MODEL,
DOMAIN,
KEY_COORDINATOR,
KEY_DEVICE,
MODEL_FAN_ZA5,
MODELS_HUMIDIFIER_MIIO,
MODELS_HUMIDIFIER_MIOT,
MODELS_HUMIDIFIER_MJJSQ,
MODELS_VACUUM,
MODELS_VACUUM_WITH_MOP,
MODELS_VACUUM_WITH_SEPARATE_MOP,
)
from .device import XiaomiCoordinatedMiioEntity
_LOGGER = logging.getLogger(__name__)
ATTR_NO_WATER = "no_water"
ATTR_POWERSUPPLY_ATTACHED = "powersupply_attached"
ATTR_WATER_TANK_DETACHED = "water_tank_detached"
ATTR_MOP_ATTACHED = "is_water_box_carriage_attached"
ATTR_WATER_BOX_ATTACHED = "is_water_box_attached"
ATTR_WATER_SHORTAGE = "is_water_shortage"
@dataclass
class XiaomiMiioBinarySensorDescription(BinarySensorEntityDescription):
"""A class that describes binary sensor entities."""
value: Callable | None = None
parent_key: str | None = None
BINARY_SENSOR_TYPES = (
XiaomiMiioBinarySensorDescription(
key=ATTR_NO_WATER,
name="Water Tank Empty",
icon="mdi:water-off-outline",
entity_category=EntityCategory.DIAGNOSTIC,
),
XiaomiMiioBinarySensorDescription(
key=ATTR_WATER_TANK_DETACHED,
name="Water Tank",
icon="mdi:car-coolant-level",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
value=lambda value: not value,
entity_category=EntityCategory.DIAGNOSTIC,
),
XiaomiMiioBinarySensorDescription(
key=ATTR_POWERSUPPLY_ATTACHED,
name="Power Supply",
device_class=BinarySensorDeviceClass.PLUG,
entity_category=EntityCategory.DIAGNOSTIC,
),
)
FAN_ZA5_BINARY_SENSORS = (ATTR_POWERSUPPLY_ATTACHED,)
VACUUM_SENSORS = {
ATTR_MOP_ATTACHED: XiaomiMiioBinarySensorDescription(
key=ATTR_WATER_BOX_ATTACHED,
name="Mop Attached",
icon="mdi:square-rounded",
parent_key=VacuumCoordinatorDataAttributes.status,
entity_registry_enabled_default=True,
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
),
ATTR_WATER_BOX_ATTACHED: XiaomiMiioBinarySensorDescription(
key=ATTR_WATER_BOX_ATTACHED,
name="Water Box Attached",
icon="mdi:water",
parent_key=VacuumCoordinatorDataAttributes.status,
entity_registry_enabled_default=True,
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
),
ATTR_WATER_SHORTAGE: XiaomiMiioBinarySensorDescription(
key=ATTR_WATER_SHORTAGE,
name="Water Shortage",
icon="mdi:water",
parent_key=VacuumCoordinatorDataAttributes.status,
entity_registry_enabled_default=True,
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
),
}
VACUUM_SENSORS_SEPARATE_MOP = {
**VACUUM_SENSORS,
ATTR_MOP_ATTACHED: XiaomiMiioBinarySensorDescription(
key=ATTR_MOP_ATTACHED,
name="Mop Attached",
icon="mdi:square-rounded",
parent_key=VacuumCoordinatorDataAttributes.status,
entity_registry_enabled_default=True,
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
),
}
HUMIDIFIER_MIIO_BINARY_SENSORS = (ATTR_WATER_TANK_DETACHED,)
HUMIDIFIER_MIOT_BINARY_SENSORS = (ATTR_WATER_TANK_DETACHED,)
HUMIDIFIER_MJJSQ_BINARY_SENSORS = (ATTR_NO_WATER, ATTR_WATER_TANK_DETACHED)
def _setup_vacuum_sensors(hass, config_entry, async_add_entities):
"""Only vacuums with mop should have binary sensor registered."""
if config_entry.data[CONF_MODEL] not in MODELS_VACUUM_WITH_MOP:
return
device = hass.data[DOMAIN][config_entry.entry_id].get(KEY_DEVICE)
coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR]
entities = []
sensors = VACUUM_SENSORS
if config_entry.data[CONF_MODEL] in MODELS_VACUUM_WITH_SEPARATE_MOP:
sensors = VACUUM_SENSORS_SEPARATE_MOP
for sensor, description in sensors.items():
parent_key_data = getattr(coordinator.data, description.parent_key)
if getattr(parent_key_data, description.key, None) is None:
_LOGGER.debug(
"It seems the %s does not support the %s as the initial value is None",
config_entry.data[CONF_MODEL],
description.key,
)
continue
entities.append(
XiaomiGenericBinarySensor(
f"{config_entry.title} {description.name}",
device,
config_entry,
f"{sensor}_{config_entry.unique_id}",
coordinator,
description,
)
)
async_add_entities(entities)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Xiaomi sensor from a config entry."""
entities = []
if config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
model = config_entry.data[CONF_MODEL]
sensors = []
if model in MODEL_FAN_ZA5:
sensors = FAN_ZA5_BINARY_SENSORS
elif model in MODELS_HUMIDIFIER_MIIO:
sensors = HUMIDIFIER_MIIO_BINARY_SENSORS
elif model in MODELS_HUMIDIFIER_MIOT:
sensors = HUMIDIFIER_MIOT_BINARY_SENSORS
elif model in MODELS_HUMIDIFIER_MJJSQ:
sensors = HUMIDIFIER_MJJSQ_BINARY_SENSORS
elif model in MODELS_VACUUM:
return _setup_vacuum_sensors(hass, config_entry, async_add_entities)
for description in BINARY_SENSOR_TYPES:
if description.key not in sensors:
continue
entities.append(
XiaomiGenericBinarySensor(
f"{config_entry.title} {description.name}",
hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE],
config_entry,
f"{description.key}_{config_entry.unique_id}",
hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR],
description,
)
)
async_add_entities(entities)
class XiaomiGenericBinarySensor(XiaomiCoordinatedMiioEntity, BinarySensorEntity):
"""Representation of a Xiaomi Humidifier binary sensor."""
entity_description: XiaomiMiioBinarySensorDescription
def __init__(self, name, device, entry, unique_id, coordinator, description):
"""Initialize the entity."""
super().__init__(name, device, entry, unique_id, coordinator)
self.entity_description = description
self._attr_entity_registry_enabled_default = (
description.entity_registry_enabled_default
)
self._attr_is_on = self._determine_native_value()
@callback
def _handle_coordinator_update(self) -> None:
self._attr_is_on = self._determine_native_value()
super()._handle_coordinator_update()
def _determine_native_value(self):
"""Determine native value."""
if self.entity_description.parent_key is not None:
return self._extract_value_from_attribute(
getattr(self.coordinator.data, self.entity_description.parent_key),
self.entity_description.key,
)
state = self._extract_value_from_attribute(
self.coordinator.data, self.entity_description.key
)
if self.entity_description.value is not None and state is not None:
return self.entity_description.value(state)
return state