diff --git a/custom_components/midea_auto_cloud/climate.py b/custom_components/midea_auto_cloud/climate.py index def8ad4..fd4fb30 100644 --- a/custom_components/midea_auto_cloud/climate.py +++ b/custom_components/midea_auto_cloud/climate.py @@ -13,7 +13,6 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -from .core.logger import MideaLogger from .midea_entity import MideaEntity from . import load_device_config @@ -66,10 +65,6 @@ class MideaClimateEntity(MideaEntity, ClimateEntity): rationale=rationale, config=config, ) - self._device = device - self._manufacturer = manufacturer - self._rationale = rationale - self._config = config self._key_power = self._config.get("power") self._key_hvac_modes = self._config.get("hvac_modes") self._key_preset_modes = self._config.get("preset_modes") diff --git a/custom_components/midea_auto_cloud/device_mapping/T0xED.py b/custom_components/midea_auto_cloud/device_mapping/T0xED.py index a3d5090..872312c 100644 --- a/custom_components/midea_auto_cloud/device_mapping/T0xED.py +++ b/custom_components/midea_auto_cloud/device_mapping/T0xED.py @@ -1,4 +1,5 @@ -from homeassistant.const import Platform, UnitOfTemperature, PRECISION_HALVES, UnitOfTime, UnitOfElectricPotential, UnitOfVolume, UnitOfMass +from homeassistant.const import Platform, UnitOfTemperature, PRECISION_HALVES, UnitOfTime, UnitOfElectricPotential, \ + UnitOfVolume, UnitOfMass from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.switch import SwitchDeviceClass @@ -10,61 +11,44 @@ DEVICE_MAPPING = { "centralized": [], "entities": { Platform.SWITCH: { - "holiday_mode": { + "power": { "device_class": SwitchDeviceClass.SWITCH, }, - "water_way": { + "heat_start": { + "device_class": SwitchDeviceClass.SWITCH, + "rationale": [0, 1], + }, + "lock": { "device_class": SwitchDeviceClass.SWITCH, }, - "soften": { + "sleep": { "device_class": SwitchDeviceClass.SWITCH, }, - "leak_water_protection": { + "keep_warm": { "device_class": SwitchDeviceClass.SWITCH, }, - "cl_sterilization": { + "vacation": { "device_class": SwitchDeviceClass.SWITCH, }, - "micro_leak": { + "germicidal": { "device_class": SwitchDeviceClass.SWITCH, }, - "low_salt": { + "lack_water": { "device_class": SwitchDeviceClass.SWITCH, }, - "no_salt": { + "drainage": { "device_class": SwitchDeviceClass.SWITCH, }, - "low_battery": { + "wash_enable": { "device_class": SwitchDeviceClass.SWITCH, }, - "salt_level_sensor_error": { - "device_class": SwitchDeviceClass.SWITCH, - }, - "flowmeter_error": { - "device_class": SwitchDeviceClass.SWITCH, - }, - "leak_water": { - "device_class": SwitchDeviceClass.SWITCH, - }, - "micro_leak_protection": { - "device_class": SwitchDeviceClass.SWITCH, - }, - "maintenance_reminder_switch": { - "device_class": SwitchDeviceClass.SWITCH, - }, - "rsj_stand_by": { - "device_class": SwitchDeviceClass.SWITCH, - }, - "regeneration": { - "device_class": SwitchDeviceClass.SWITCH, - }, - "pre_regeneration": { - "device_class": SwitchDeviceClass.SWITCH, - } }, Platform.BINARY_SENSOR: { - "maintenance_remind": { - "device_class": BinarySensorDeviceClass.PROBLEM, + "heat_status": { + "device_class": BinarySensorDeviceClass.RUNNING, + }, + "standby_status": { + "device_class": BinarySensorDeviceClass.RUNNING, }, "chlorine_sterilization_error": { "device_class": BinarySensorDeviceClass.PROBLEM, @@ -74,154 +58,31 @@ DEVICE_MAPPING = { } }, Platform.SENSOR: { - "micro_leak_protection_value": { - "device_class": SensorDeviceClass.PRESSURE, - "unit_of_measurement": "kPa", + "current_temperature": { + "device_class": SensorDeviceClass.TEMPERATURE, + "unit_of_measurement": UnitOfTemperature.CELSIUS, "state_class": SensorStateClass.MEASUREMENT }, - "regeneration_current_stages": { - "device_class": SensorDeviceClass.ENUM + "cool_target_temperature": { + "device_class": SensorDeviceClass.TEMPERATURE, + "unit_of_measurement": UnitOfTemperature.CELSIUS, + "state_class": SensorStateClass.MEASUREMENT }, - "water_hardness": { - "device_class": SensorDeviceClass.WATER, + "water_consumption_ml": { + "device_class": SensorDeviceClass.VOLUME, "unit_of_measurement": UnitOfVolume.LITERS, "state_class": SensorStateClass.TOTAL_INCREASING }, - "timing_regeneration_hour": { - "device_class": SensorDeviceClass.DURATION, - "unit_of_measurement": UnitOfTime.HOURS, - "state_class": SensorStateClass.MEASUREMENT - }, - "real_time_setting_hour": { - "device_class": SensorDeviceClass.DURATION, - "unit_of_measurement": UnitOfTime.HOURS, - "state_class": SensorStateClass.MEASUREMENT - }, - "timing_regeneration_min": { + "keep_warm_time": { "device_class": SensorDeviceClass.DURATION, "unit_of_measurement": UnitOfTime.MINUTES, "state_class": SensorStateClass.MEASUREMENT }, - "regeneration_left_seconds": { - "device_class": SensorDeviceClass.DURATION, - "unit_of_measurement": UnitOfTime.SECONDS, - "state_class": SensorStateClass.MEASUREMENT - }, - "maintenance_reminder_setting": { - "device_class": SensorDeviceClass.ENUM - }, - "mixed_water_gear": { - "device_class": SensorDeviceClass.ENUM - }, - "use_days": { - "device_class": SensorDeviceClass.DURATION, - "unit_of_measurement": UnitOfTime.DAYS, - "state_class": SensorStateClass.MEASUREMENT - }, - "days_since_last_regeneration": { - "device_class": SensorDeviceClass.DURATION, - "unit_of_measurement": UnitOfTime.DAYS, - "state_class": SensorStateClass.MEASUREMENT - }, - "velocity": { - "device_class": SensorDeviceClass.SPEED, - "unit_of_measurement": "m/s", - "state_class": SensorStateClass.MEASUREMENT - }, - "supply_voltage": { - "device_class": SensorDeviceClass.VOLTAGE, - "unit_of_measurement": UnitOfElectricPotential.VOLT, - "state_class": SensorStateClass.MEASUREMENT - }, - "left_salt": { - "device_class": SensorDeviceClass.WEIGHT, - "unit_of_measurement": UnitOfMass.KILOGRAMS, - "state_class": SensorStateClass.MEASUREMENT - }, - "pre_regeneration_days": { - "device_class": SensorDeviceClass.DURATION, - "unit_of_measurement": UnitOfTime.DAYS, - "state_class": SensorStateClass.MEASUREMENT - }, - "flushing_days": { - "device_class": SensorDeviceClass.DURATION, - "unit_of_measurement": UnitOfTime.DAYS, - "state_class": SensorStateClass.MEASUREMENT - }, - "salt_setting": { - "device_class": SensorDeviceClass.ENUM - }, - "regeneration_count": { - "device_class": SensorDeviceClass.ENUM - }, - "battery_voltage": { - "device_class": SensorDeviceClass.VOLTAGE, - "unit_of_measurement": UnitOfElectricPotential.VOLT, - "state_class": SensorStateClass.MEASUREMENT - }, - "error": { - "device_class": SensorDeviceClass.ENUM - }, - "days_since_last_two_regeneration": { - "device_class": SensorDeviceClass.DURATION, - "unit_of_measurement": UnitOfTime.DAYS, - "state_class": SensorStateClass.MEASUREMENT - }, - "remind_maintenance_days": { - "device_class": SensorDeviceClass.DURATION, - "unit_of_measurement": UnitOfTime.DAYS, - "state_class": SensorStateClass.MEASUREMENT - }, - "real_date_setting_year": { - "device_class": SensorDeviceClass.ENUM - }, - "real_date_setting_month": { - "device_class": SensorDeviceClass.ENUM - }, - "real_date_setting_day": { - "device_class": SensorDeviceClass.ENUM - }, - "category": { - "device_class": SensorDeviceClass.ENUM - }, - "real_time_setting_min": { + "warm_left_time": { "device_class": SensorDeviceClass.DURATION, "unit_of_measurement": UnitOfTime.MINUTES, "state_class": SensorStateClass.MEASUREMENT }, - "regeneration_stages": { - "device_class": SensorDeviceClass.ENUM - }, - "soft_available_big": { - "device_class": SensorDeviceClass.VOLUME, - "unit_of_measurement": UnitOfVolume.LITERS, - "state_class": SensorStateClass.TOTAL_INCREASING - }, - "water_consumption_big": { - "device_class": SensorDeviceClass.VOLUME, - "unit_of_measurement": UnitOfVolume.LITERS, - "state_class": SensorStateClass.TOTAL_INCREASING - }, - "water_consumption_today": { - "device_class": SensorDeviceClass.VOLUME, - "unit_of_measurement": UnitOfVolume.LITERS, - "state_class": SensorStateClass.TOTAL_INCREASING - }, - "water_consumption_average": { - "device_class": SensorDeviceClass.VOLUME, - "unit_of_measurement": UnitOfVolume.LITERS, - "state_class": SensorStateClass.TOTAL_INCREASING - }, - "salt_alarm_threshold": { - "device_class": SensorDeviceClass.WEIGHT, - "unit_of_measurement": UnitOfMass.KILOGRAMS, - "state_class": SensorStateClass.MEASUREMENT - }, - "leak_water_protection_value": { - "device_class": SensorDeviceClass.PRESSURE, - "unit_of_measurement": "kPa", - "state_class": SensorStateClass.MEASUREMENT - } } } } diff --git a/custom_components/midea_auto_cloud/fan.py b/custom_components/midea_auto_cloud/fan.py index d5f1287..5f08295 100644 --- a/custom_components/midea_auto_cloud/fan.py +++ b/custom_components/midea_auto_cloud/fan.py @@ -44,10 +44,6 @@ class MideaFanEntity(MideaEntity, FanEntity): rationale=rationale, config=config, ) - self._device = device - self._manufacturer = manufacturer - self._rationale = rationale - self._config = config self._key_power = self._config.get("power") self._key_preset_modes = self._config.get("preset_modes") self._key_speeds = self._config.get("speeds") diff --git a/custom_components/midea_auto_cloud/humidifier.py b/custom_components/midea_auto_cloud/humidifier.py index dbd4483..93648db 100644 --- a/custom_components/midea_auto_cloud/humidifier.py +++ b/custom_components/midea_auto_cloud/humidifier.py @@ -61,11 +61,6 @@ class MideaHumidifierEntity(MideaEntity, HumidifierEntity): rationale=rationale, config=config, ) - self._device = device - self._manufacturer = manufacturer - self._rationale = rationale - self._entity_key = entity_key - self._config = config @property def device_class(self): diff --git a/custom_components/midea_auto_cloud/midea_entity.py b/custom_components/midea_auto_cloud/midea_entity.py index dd4a2ea..6f232a9 100644 --- a/custom_components/midea_auto_cloud/midea_entity.py +++ b/custom_components/midea_auto_cloud/midea_entity.py @@ -3,6 +3,7 @@ from __future__ import annotations import logging +from enum import IntEnum from typing import Any from homeassistant.helpers.debounce import Debouncer @@ -16,6 +17,10 @@ from .data_coordinator import MideaDataUpdateCoordinator _LOGGER = logging.getLogger(__name__) +class Rationale(IntEnum): + EQUALLY = 0 + GREATER = 1 + LESS = 2 class MideaEntity(CoordinatorEntity[MideaDataUpdateCoordinator], Entity): """Base class for Midea entities.""" @@ -61,6 +66,7 @@ class MideaEntity(CoordinatorEntity[MideaDataUpdateCoordinator], Entity): self._attr_unique_id = f"{DOMAIN}.{self._device_id}_{self._entity_key}" self.entity_id_base = f"midea_{self._device_id}" manu = "Midea" if manufacturer is None else manufacturer + self.manufacturer = manu self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, str(self._device_id))}, model=self._model, @@ -140,78 +146,65 @@ class MideaEntity(CoordinatorEntity[MideaDataUpdateCoordinator], Entity): Accepts common truthy representations: True/1/"on"/"true". """ + result = False if attribute_key is None: - return False - value = self.device_attributes.get(attribute_key) - if isinstance(value, bool): - return value - return value in (1, "1", "on", "ON", "true", "TRUE") + return result + status = self.device_attributes.get(attribute_key) + if status is not None: + try: + result = bool(self._rationale.index(status)) + except ValueError: + MideaLogger.error(f"The value of attribute {attribute_key} ('{status}') " + f"is not in rationale {self._rationale}") + return result async def _async_set_status_on_off(self, attribute_key: str | None, turn_on: bool) -> None: """Set boolean attribute via coordinator, no-op if key is None.""" if attribute_key is None: return - await self.async_set_attribute(attribute_key, bool(turn_on)) + MideaLogger.error(f"self._rationale: {self._rationale}, {int(turn_on)}") + await self.async_set_attribute(attribute_key, self._rationale[int(turn_on)]) - def _list_get_selected(self, options: list[dict] | None, rationale: object = None) -> int | None: - """Select index from a list of dict conditions matched against attributes. - - The optional rationale supports equality/greater/less matching. It can be - a string name ("EQUALLY"/"GREATER"/"LESS") or an Enum with .name. - """ - if not options: - return None - - rationale_name = getattr(rationale, "name", None) or rationale or "EQUALLY" - for index in range(0, len(options)): + def _list_get_selected(self, key_of_list: list, rationale: Rationale = Rationale.EQUALLY): + for index in range(0, len(key_of_list)): match = True - for attr, expected in options[index].items(): - current = self.device_attributes.get(attr) - if current is None: + for attr, value in key_of_list[index].items(): + state_value = self.device_attributes.get(attr) + if state_value is None: match = False break - if rationale_name == "EQUALLY" and current != expected: + if rationale is Rationale.EQUALLY and state_value != value: match = False break - if rationale_name == "GREATER" and current < expected: + if rationale is Rationale.GREATER and state_value < value: match = False break - if rationale_name == "LESS" and current > expected: + if rationale is Rationale.LESS and state_value > value: match = False break if match: return index return None - def _dict_get_selected(self, mapping: dict | None, rationale: object = None): - """Return key from a dict whose value (a condition dict) matches attributes. - - The optional rationale supports equality/greater/less matching. It can be - a string name ("EQUALLY"/"GREATER"/"LESS") or an Enum with .name. - """ - if not mapping: - return None - rationale_name = getattr(rationale, "name", None) or rationale or "EQUALLY" - for key, conditions in mapping.items(): - if not isinstance(conditions, dict): - continue + def _dict_get_selected(self, key_of_dict: dict, rationale: Rationale = Rationale.EQUALLY): + for mode, status in key_of_dict.items(): match = True - for attr, expected in conditions.items(): - current = self.device_attributes.get(attr) - if current is None: + for attr, value in status.items(): + state_value = self.device_attributes.get(attr) + if state_value is None: match = False break - if rationale_name == "EQUALLY" and current != expected: + if rationale is Rationale.EQUALLY and state_value != value: match = False break - if rationale_name == "GREATER" and current <= expected: + if rationale is Rationale.GREATER and state_value < value: match = False break - if rationale_name == "LESS" and current >= expected: + if rationale is Rationale.LESS and state_value > value: match = False break if match: - return key + return mode return None async def publish_command_from_current_state(self) -> None: diff --git a/custom_components/midea_auto_cloud/select.py b/custom_components/midea_auto_cloud/select.py index 560ac5a..02c734f 100644 --- a/custom_components/midea_auto_cloud/select.py +++ b/custom_components/midea_auto_cloud/select.py @@ -44,10 +44,6 @@ class MideaSelectEntity(MideaEntity, SelectEntity): rationale=rationale, config=config, ) - self._device = device - self._manufacturer = manufacturer - self._rationale = rationale - self._config = config self._key_options = self._config.get("options") @property diff --git a/custom_components/midea_auto_cloud/sensor.py b/custom_components/midea_auto_cloud/sensor.py index 9b227be..e0faeff 100644 --- a/custom_components/midea_auto_cloud/sensor.py +++ b/custom_components/midea_auto_cloud/sensor.py @@ -57,10 +57,6 @@ class MideaSensorEntity(MideaEntity, SensorEntity): rationale=rationale, config=config, ) - self._device = device - self._manufacturer = manufacturer - self._rationale = rationale - self._config = config @property def native_value(self): diff --git a/custom_components/midea_auto_cloud/switch.py b/custom_components/midea_auto_cloud/switch.py index ba70b91..41d6952 100644 --- a/custom_components/midea_auto_cloud/switch.py +++ b/custom_components/midea_auto_cloud/switch.py @@ -58,24 +58,16 @@ class MideaSwitchEntity(MideaEntity, SwitchEntity): rationale=rationale, config=config, ) - self._device = device - self._manufacturer = manufacturer - self._rationale = rationale - self._config = config - @property def is_on(self) -> bool: """Return if the switch is on.""" - value = self.device_attributes.get(self._entity_key) - if isinstance(value, bool): - return value - return value == 1 or value == "on" or value == "true" + return self._get_status_on_off(self._entity_key) async def async_turn_on(self): """Turn the switch on.""" - await self.async_set_attribute(self._entity_key, True) + await self._async_set_status_on_off(self._entity_key, True) async def async_turn_off(self): """Turn the switch off.""" - await self.async_set_attribute(self._entity_key, False) + await self._async_set_status_on_off(self._entity_key, False) diff --git a/custom_components/midea_auto_cloud/translations/en.json b/custom_components/midea_auto_cloud/translations/en.json index a1c2c10..f3f3584 100644 --- a/custom_components/midea_auto_cloud/translations/en.json +++ b/custom_components/midea_auto_cloud/translations/en.json @@ -1075,6 +1075,18 @@ }, "ud_diy_up_percent": { "name": "UD DIY Up Percent" + }, + "water_consumption_ml": { + "name": "Water Consumption (ml)" + }, + "cool_target_temperature": { + "name": "Cool Target Temperature" + }, + "keep_warm_time": { + "name": "Keep Warm Time" + }, + "warm_left_time": { + "name": "Warm Left Time" } }, "binary_sensor": { @@ -1233,6 +1245,12 @@ }, "filter_reset": { "name": "Filter Reset" + }, + "heat_status": { + "name": "Heat Status" + }, + "standby_status": { + "name": "Standby Status" } }, "climate": { @@ -1978,6 +1996,24 @@ }, "unfreeze_power": { "name": "Unfreeze Power" + }, + "heat_start": { + "name": "Heat Start" + }, + "keep_warm": { + "name": "Keep Warm" + }, + "vacation": { + "name": "Vacation Mode" + }, + "germicidal": { + "name": "Germicidal" + }, + "drainage": { + "name": "Drainage" + }, + "wash_enable": { + "name": "Wash Enable" } } } diff --git a/custom_components/midea_auto_cloud/translations/zh-Hans.json b/custom_components/midea_auto_cloud/translations/zh-Hans.json index deff5f2..de34dff 100644 --- a/custom_components/midea_auto_cloud/translations/zh-Hans.json +++ b/custom_components/midea_auto_cloud/translations/zh-Hans.json @@ -269,6 +269,12 @@ }, "filter_reset": { "name": "滤网重置" + }, + "heat_status": { + "name": "加热状态" + }, + "standby_status": { + "name": "待机状态" } }, "climate": { @@ -1261,6 +1267,18 @@ }, "ud_diy_up_percent": { "name": "上下自定义上百分比" + }, + "water_consumption_ml": { + "name": "用水量(毫升)" + }, + "cool_target_temperature": { + "name": "制冷目标温度" + }, + "keep_warm_time": { + "name": "保温时间" + }, + "warm_left_time": { + "name": "剩余加热时间" } }, "switch": { @@ -1983,6 +2001,24 @@ }, "unfreeze_power": { "name": "解冻电源" + }, + "heat_start": { + "name": "加热启动" + }, + "keep_warm": { + "name": "保温" + }, + "vacation": { + "name": "度假模式" + }, + "germicidal": { + "name": "杀菌" + }, + "drainage": { + "name": "排水" + }, + "wash_enable": { + "name": "洗涤启用" } } } diff --git a/custom_components/midea_auto_cloud/water_heater.py b/custom_components/midea_auto_cloud/water_heater.py index cce82a7..1c302b5 100644 --- a/custom_components/midea_auto_cloud/water_heater.py +++ b/custom_components/midea_auto_cloud/water_heater.py @@ -56,10 +56,6 @@ class MideaWaterHeaterEntityEntity(MideaEntity, WaterHeaterEntity): rationale=rationale, config=config, ) - self._device = device - self._manufacturer = manufacturer - self._rationale = rationale - self._config = config self._key_power = self._config.get("power") self._key_operation_list = self._config.get("operation_list") self._key_min_temp = self._config.get("min_temp")