Add Aqara E1 thermostat entities to ZHA (#90158)

* Add Aqara E1 thermostat entities (WIP)

* Remove calibrate button for now

* Add diagnostic entity category to calibrated + external sensor

* Add multiplier for ZHA config number/away preset temp

* Set default multiplier correctly

* Add and use `CONFIG_DIAGNOSTIC_MATCH` for diagnostic entities
This commit is contained in:
TheJulianJES 2023-03-29 03:12:21 +02:00 committed by GitHub
parent 8660035027
commit 12f49006cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 4 deletions

View file

@ -8,7 +8,7 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -39,6 +39,9 @@ CLASS_MAPPING = {
STRICT_MATCH = functools.partial(ZHA_ENTITIES.strict_match, Platform.BINARY_SENSOR)
MULTI_MATCH = functools.partial(ZHA_ENTITIES.multipass_match, Platform.BINARY_SENSOR)
CONFIG_DIAGNOSTIC_MATCH = functools.partial(
ZHA_ENTITIES.config_diagnostic_match, Platform.BINARY_SENSOR
)
async def async_setup_entry(
@ -201,3 +204,39 @@ class XiaomiPlugConsumerConnected(BinarySensor, id_suffix="consumer_connected"):
SENSOR_ATTR = "consumer_connected"
_attr_name: str = "Consumer connected"
_attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.PLUG
@MULTI_MATCH(channel_names="opple_cluster", models={"lumi.airrtc.agl001"})
class AqaraThermostatWindowOpen(BinarySensor, id_suffix="window_open"):
"""ZHA Aqara thermostat window open binary sensor."""
SENSOR_ATTR = "window_open"
_attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.WINDOW
_attr_name: str = "Window open"
@MULTI_MATCH(channel_names="opple_cluster", models={"lumi.airrtc.agl001"})
class AqaraThermostatValveAlarm(BinarySensor, id_suffix="valve_alarm"):
"""ZHA Aqara thermostat valve alarm binary sensor."""
SENSOR_ATTR = "valve_alarm"
_attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.PROBLEM
_attr_name: str = "Valve alarm"
@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.airrtc.agl001"})
class AqaraThermostatCalibrated(BinarySensor, id_suffix="calibrated"):
"""ZHA Aqara thermostat calibrated binary sensor."""
SENSOR_ATTR = "calibrated"
_attr_entity_category: EntityCategory = EntityCategory.DIAGNOSTIC
_attr_name: str = "Calibrated"
@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.airrtc.agl001"})
class AqaraThermostatExternalSensor(BinarySensor, id_suffix="sensor"):
"""ZHA Aqara thermostat external sensor binary sensor."""
SENSOR_ATTR = "sensor"
_attr_entity_category: EntityCategory = EntityCategory.DIAGNOSTIC
_attr_name: str = "External sensor"

View file

@ -138,6 +138,20 @@ class OppleRemote(ZigbeeChannel):
"serving_size": True,
"portion_weight": True,
}
elif self.cluster.endpoint.model == "lumi.airrtc.agl001":
self.ZCL_INIT_ATTRS = {
"system_mode": True,
"preset": True,
"window_detection": True,
"valve_detection": True,
"valve_alarm": True,
"child_lock": True,
"away_preset_temperature": True,
"window_open": True,
"calibrated": True,
"schedule": True,
"sensor": True,
}
async def async_initialize_channel_specific(self, from_cache: bool) -> None:
"""Initialize channel specific."""

View file

@ -11,7 +11,7 @@ from zigpy.zcl.foundation import Status
from homeassistant.components.number import NumberEntity, NumberMode
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, Platform, UnitOfMass
from homeassistant.const import EntityCategory, Platform, UnitOfMass, UnitOfTemperature
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -375,6 +375,7 @@ class ZHANumberConfigurationEntity(ZhaEntity, NumberEntity):
_attr_entity_category = EntityCategory.CONFIG
_attr_native_step: float = 1.0
_attr_multiplier: float = 1
_zcl_attribute: str
@classmethod
@ -417,13 +418,13 @@ class ZHANumberConfigurationEntity(ZhaEntity, NumberEntity):
@property
def native_value(self) -> float:
"""Return the current value."""
return self._channel.cluster.get(self._zcl_attribute)
return self._channel.cluster.get(self._zcl_attribute) * self._attr_multiplier
async def async_set_native_value(self, value: float) -> None:
"""Update the current value from HA."""
try:
res = await self._channel.cluster.write_attributes(
{self._zcl_attribute: int(value)}
{self._zcl_attribute: int(value / self._attr_multiplier)}
)
except zigpy.exceptions.ZigbeeException as ex:
self.error("Could not set value: %s", ex)
@ -861,3 +862,20 @@ class AqaraPetFeederPortionWeight(
_attr_mode: NumberMode = NumberMode.BOX
_attr_native_unit_of_measurement: str = UnitOfMass.GRAMS
_attr_icon: str = "mdi:weight-gram"
@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.airrtc.agl001"})
class AqaraThermostatAwayTemp(
ZHANumberConfigurationEntity, id_suffix="away_preset_temperature"
):
"""Aqara away preset temperature configuration entity."""
_attr_entity_category = EntityCategory.CONFIG
_attr_native_min_value: float = 5
_attr_native_max_value: float = 30
_attr_multiplier: float = 0.01
_zcl_attribute: str = "away_preset_temperature"
_attr_name: str = "Away preset temperature"
_attr_mode: NumberMode = NumberMode.SLIDER
_attr_native_unit_of_measurement: str = UnitOfTemperature.CELSIUS
_attr_icon: str = ICONS[0]

View file

@ -503,3 +503,20 @@ class AqaraPetFeederMode(ZCLEnumSelectEntity, id_suffix="feeding_mode"):
_enum = AqaraFeedingMode
_attr_name = "Mode"
_attr_icon: str = "mdi:wrench-clock"
class AqaraThermostatPresetMode(types.enum8):
"""Thermostat preset mode."""
Manual = 0x00
Auto = 0x01
Away = 0x02
@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.airrtc.agl001"})
class AqaraThermostatPreset(ZCLEnumSelectEntity, id_suffix="preset"):
"""Representation of an Aqara thermostat preset configuration entity."""
_select_attr = "preset"
_enum = AqaraThermostatPresetMode
_attr_name = "Preset"

View file

@ -477,3 +477,32 @@ class TuyaChildLockSwitch(ZHASwitchConfigurationEntity, id_suffix="child_lock"):
_zcl_attribute: str = "child_lock"
_attr_name = "Child lock"
_attr_icon: str = "mdi:account-lock"
@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.airrtc.agl001"})
class AqaraThermostatWindowDetection(
ZHASwitchConfigurationEntity, id_suffix="window_detection"
):
"""Representation of an Aqara thermostat window detection configuration entity."""
_zcl_attribute: str = "window_detection"
_attr_name = "Window detection"
@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.airrtc.agl001"})
class AqaraThermostatValveDetection(
ZHASwitchConfigurationEntity, id_suffix="valve_detection"
):
"""Representation of an Aqara thermostat valve detection configuration entity."""
_zcl_attribute: str = "valve_detection"
_attr_name = "Valve detection"
@CONFIG_DIAGNOSTIC_MATCH(channel_names="opple_cluster", models={"lumi.airrtc.agl001"})
class AqaraThermostatChildLock(ZHASwitchConfigurationEntity, id_suffix="child_lock"):
"""Representation of an Aqara thermostat child lock configuration entity."""
_zcl_attribute: str = "child_lock"
_attr_name = "Child lock"
_attr_icon: str = "mdi:account-lock"