From f2735fd729ba15ac7ea629432d0fdf4e09002245 Mon Sep 17 00:00:00 2001 From: sususweet Date: Fri, 31 Oct 2025 22:46:29 +0800 Subject: [PATCH] feat: add new entity include button and number. --- custom_components/midea_auto_cloud/button.py | 99 ++++++++++++++++ custom_components/midea_auto_cloud/number.py | 117 +++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 custom_components/midea_auto_cloud/button.py create mode 100644 custom_components/midea_auto_cloud/number.py diff --git a/custom_components/midea_auto_cloud/button.py b/custom_components/midea_auto_cloud/button.py new file mode 100644 index 0000000..c36a578 --- /dev/null +++ b/custom_components/midea_auto_cloud/button.py @@ -0,0 +1,99 @@ +from homeassistant.components.button import ButtonEntity +from homeassistant.const import Platform +from homeassistant.config_entries import ConfigEntry +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 + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up button entities for Midea devices.""" + account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id) + if not account_bucket: + async_add_entities([]) + return + device_list = account_bucket.get("device_list", {}) + coordinator_map = account_bucket.get("coordinator_map", {}) + + devs = [] + for device_id, info in device_list.items(): + device_type = info.get("type") + sn8 = info.get("sn8") + config = await load_device_config(hass, device_type, sn8) or {} + entities_cfg = (config.get("entities") or {}).get(Platform.BUTTON, {}) + manufacturer = config.get("manufacturer") + rationale = config.get("rationale") + coordinator = coordinator_map.get(device_id) + device = coordinator.device if coordinator else None + for entity_key, ecfg in entities_cfg.items(): + devs.append(MideaButtonEntity( + coordinator, device, manufacturer, rationale, entity_key, ecfg + )) + async_add_entities(devs) + + +class MideaButtonEntity(MideaEntity, ButtonEntity): + """Midea button entity.""" + + def __init__(self, coordinator, device, manufacturer, rationale, entity_key, config): + super().__init__( + coordinator, + device.device_id, + device.device_name, + f"T0x{device.device_type:02X}", + device.sn, + device.sn8, + device.model, + entity_key, + device=device, + manufacturer=manufacturer, + rationale=rationale, + config=config, + ) + + async def async_press(self) -> None: + """Handle the button press.""" + # 从配置中获取要执行的命令或操作 + command = self._config.get("command") + attribute = self._config.get("attribute", self._entity_key) + value = self._config.get("value") + + # 判断是否为中央空调设备(T0x21) + is_central_ac = self._device.device_type == 0x21 if self._device else False + + if command: + # 如果配置中指定了命令,执行该命令 + if isinstance(command, dict): + # 如果是字典,可能需要发送多个属性 + await self.async_set_attributes(command) + elif isinstance(command, str): + # 如果是字符串,可能是特殊命令类型 + await self._async_execute_command(command) + elif value is not None: + # 如果配置中指定了值,设置该属性值 + await self.async_set_attribute(attribute, value) + else: + # 默认行为:如果没有指定命令或值,记录警告 + MideaLogger.warning( + f"Button {self._entity_key} has no command or value configured" + ) + + async def _async_execute_command(self, command: str) -> None: + """Execute a special command.""" + # 这里可以处理特殊的命令类型 + # 例如:重启、重置、测试等 + if command == "reset" or command == "restart": + # 可以在这里实现重置或重启逻辑 + MideaLogger.debug(f"Executing {command} command for button {self._entity_key}") + else: + # 对于其他命令,可以通过 coordinator 发送 + await self.coordinator.async_send_command(0, command) + diff --git a/custom_components/midea_auto_cloud/number.py b/custom_components/midea_auto_cloud/number.py new file mode 100644 index 0000000..c0733f3 --- /dev/null +++ b/custom_components/midea_auto_cloud/number.py @@ -0,0 +1,117 @@ +from homeassistant.components.number import NumberEntity +from homeassistant.const import Platform +from homeassistant.config_entries import ConfigEntry +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 + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up number entities for Midea devices.""" + account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id) + if not account_bucket: + async_add_entities([]) + return + device_list = account_bucket.get("device_list", {}) + coordinator_map = account_bucket.get("coordinator_map", {}) + + devs = [] + for device_id, info in device_list.items(): + device_type = info.get("type") + sn8 = info.get("sn8") + config = await load_device_config(hass, device_type, sn8) or {} + entities_cfg = (config.get("entities") or {}).get(Platform.NUMBER, {}) + manufacturer = config.get("manufacturer") + rationale = config.get("rationale") + coordinator = coordinator_map.get(device_id) + device = coordinator.device if coordinator else None + for entity_key, ecfg in entities_cfg.items(): + devs.append(MideaNumberEntity( + coordinator, device, manufacturer, rationale, entity_key, ecfg + )) + async_add_entities(devs) + + +class MideaNumberEntity(MideaEntity, NumberEntity): + """Midea number entity.""" + + def __init__(self, coordinator, device, manufacturer, rationale, entity_key, config): + super().__init__( + coordinator, + device.device_id, + device.device_name, + f"T0x{device.device_type:02X}", + device.sn, + device.sn8, + device.model, + entity_key, + device=device, + manufacturer=manufacturer, + rationale=rationale, + config=config, + ) + # 从配置中读取数值范围,如果没有则使用默认值 + self._min_value = self._config.get("min", 0.0) + self._max_value = self._config.get("max", 100.0) + self._step = self._config.get("step", 1.0) + self._mode = self._config.get("mode", "auto") # auto, box, slider + + @property + def native_value(self) -> float | None: + """Return the current value.""" + # Use attribute from config if available, otherwise fall back to entity_key + attribute = self._config.get("attribute", self._entity_key) + value = self._get_nested_value(attribute) + + if value is None: + return None + + # 确保返回的是数值类型 + try: + return float(value) + except (ValueError, TypeError): + MideaLogger.warning( + f"Failed to convert value '{value}' to float for number entity {self._entity_key}" + ) + return None + + @property + def native_min_value(self) -> float: + """Return the minimum value.""" + return float(self._min_value) + + @property + def native_max_value(self) -> float: + """Return the maximum value.""" + return float(self._max_value) + + @property + def native_step(self) -> float: + """Return the step value.""" + return float(self._step) + + @property + def mode(self) -> str: + """Return the mode of the number entity.""" + return self._mode + + async def async_set_native_value(self, value: float) -> None: + """Set the value of the number entity.""" + # 确保值在有效范围内 + value = max(self._min_value, min(self._max_value, value)) + + # Use attribute from config if available, otherwise fall back to entity_key + attribute = self._config.get("attribute", self._entity_key) + + # 如果配置中指定了转换函数或映射,可以在这里处理 + # 否则直接设置属性值 + await self.async_set_attribute(attribute, str(int(value))) +