17 Commits

Author SHA1 Message Date
sususweet
c58b8282cd feat: wash mode for T0xE1 2025-10-01 18:56:23 +08:00
sususweet
b3be2a7cbd feat: add status when build lua command 2025-10-01 18:48:01 +08:00
sususweet
8e08974682 feat: add turn on & off feature for T0xAC. 2025-10-01 18:35:19 +08:00
sususweet
b81ef0e007 feat: add control for newwind device. Fix #5. 2025-10-01 18:32:47 +08:00
sususweet
89f3e6e6f8 feat: version 0.1.3 2025-10-01 18:00:43 +08:00
sususweet
8cb30ebb3e feat: update device control for T0xCC and T0xAC 2025-10-01 18:00:18 +08:00
sususweet
a31c905fb5 feat: adapt to T0xAC new power save mode 2025-10-01 14:20:45 +08:00
sususweet
167d767b28 feat: version 0.1.2 2025-09-30 17:13:46 +08:00
sususweet
848a086977 fix: device control of T0xD9 2025-09-30 17:12:35 +08:00
sususweet
e4334cd936 fix: device control of T0xE2 2025-09-30 15:15:29 +08:00
sususweet
8b93c58654 feat: add device by homeid 2025-09-30 15:06:27 +08:00
sususweet
bbf4d168e7 fix: remote control for device T0xED. 2025-09-30 14:38:50 +08:00
sususweet
273d4e41bf fix: reform water heater entity. 2025-09-30 00:23:56 +08:00
sususweet
e9a6c65787 fix: release lua cjson critical limit. 2025-09-30 00:07:03 +08:00
sususweet
beaf837e3b feat: version 0.1.1 2025-09-29 23:55:43 +08:00
sususweet
a7efce2d26 feat: add indoor status of T0xAC. 2025-09-29 23:44:07 +08:00
sususweet
9ff67da49b feat: fallback to cloud control when lua script encounter error. 2025-09-29 23:07:12 +08:00
26 changed files with 1501 additions and 984 deletions

View File

@@ -41,7 +41,8 @@ from .const import (
CONF_SN8,
CONF_SN,
CONF_MODEL_NUMBER,
CONF_SERVERS, STORAGE_PATH, CONF_MANUFACTURER_CODE
CONF_SERVERS, STORAGE_PATH, CONF_MANUFACTURER_CODE,
CONF_SELECTED_HOMES
)
# 账号型:登录云端、获取设备列表,并为每台设备建立协调器(无本地控制)
from .const import CONF_PASSWORD as CONF_PASSWORD_KEY, CONF_SERVER as CONF_SERVER_KEY
@@ -131,16 +132,16 @@ async def async_setup(hass: HomeAssistant, config: ConfigType):
hass.data.setdefault(DOMAIN, {})
cjson = os.getcwd() + "/cjson.lua"
bit = os.getcwd() + "/bit.lua"
if not os.path.exists(cjson):
from .const import CJSON_LUA
cjson_lua = base64.b64decode(CJSON_LUA.encode("utf-8")).decode("utf-8")
with open(cjson, "wt") as fp:
fp.write(cjson_lua)
if not os.path.exists(bit):
from .const import BIT_LUA
bit_lua = base64.b64decode(BIT_LUA.encode("utf-8")).decode("utf-8")
with open(bit, "wt") as fp:
fp.write(bit_lua)
# if not os.path.exists(cjson):
from .const import CJSON_LUA
cjson_lua = base64.b64decode(CJSON_LUA.encode("utf-8")).decode("utf-8")
with open(cjson, "wt") as fp:
fp.write(cjson_lua)
# if not os.path.exists(bit):
from .const import BIT_LUA
bit_lua = base64.b64decode(BIT_LUA.encode("utf-8")).decode("utf-8")
with open(bit, "wt") as fp:
fp.write(bit_lua)
return True
@@ -169,7 +170,26 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN].setdefault("accounts", {})
bucket = {"device_list": {}, "coordinator_map": {}}
home_ids = list(homes.keys())
# 获取用户选择的家庭ID列表
selected_homes = config_entry.data.get(CONF_SELECTED_HOMES, [])
MideaLogger.debug(f"Selected homes from config: {selected_homes}")
MideaLogger.debug(f"Available homes keys: {list(homes.keys())}")
if not selected_homes:
# 如果没有选择,默认使用所有家庭
home_ids = list(homes.keys())
else:
# 只处理用户选择的家庭,确保类型匹配
home_ids = []
for selected_home in selected_homes:
# 尝试匹配字符串和数字类型的home_id
if selected_home in homes:
home_ids.append(selected_home)
elif str(selected_home) in homes:
home_ids.append(str(selected_home))
elif int(selected_home) in homes:
home_ids.append(int(selected_home))
MideaLogger.debug(f"Final home_ids to process: {home_ids}")
for home_id in home_ids:
appliances = await cloud.list_appliances(home_id)
@@ -218,7 +238,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
mapping = {}
try:
device.set_queries(mapping.get("queries", []))
device.set_queries(mapping.get("queries", [{}]))
except Exception:
pass
try:

View File

@@ -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")
@@ -85,7 +80,9 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
@property
def supported_features(self):
features = 0
features = ClimateEntityFeature(0)
features |= ClimateEntityFeature.TURN_ON
features |= ClimateEntityFeature.TURN_OFF
if self._key_target_temperature is not None:
features |= ClimateEntityFeature.TARGET_TEMPERATURE
if self._key_preset_modes is not None:
@@ -100,7 +97,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
@property
def current_temperature(self):
temp = self.device_attributes.get(self._key_current_temperature)
temp = self._get_nested_value(self._key_current_temperature)
if temp is not None:
try:
return float(temp)
@@ -111,8 +108,8 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
@property
def target_temperature(self):
if isinstance(self._key_target_temperature, list):
temp_int = self.device_attributes.get(self._key_target_temperature[0])
tem_dec = self.device_attributes.get(self._key_target_temperature[1])
temp_int = self._get_nested_value(self._key_target_temperature[0])
tem_dec = self._get_nested_value(self._key_target_temperature[1])
if temp_int is not None and tem_dec is not None:
try:
return float(temp_int) + float(tem_dec)
@@ -120,7 +117,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
return None
return None
else:
temp = self.device_attributes.get(self._key_target_temperature)
temp = self._get_nested_value(self._key_target_temperature)
if temp is not None:
try:
return float(temp)
@@ -164,7 +161,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
@property
def fan_mode(self):
return self._dict_get_selected(self._key_fan_modes, "EQUALLY")
return self._dict_get_selected(self._key_fan_modes)
@property
def swing_modes(self):
@@ -172,7 +169,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
@property
def swing_mode(self):
return self._dict_get_selected(self._key_swing_modes, "EQUALLY")
return self._dict_get_selected(self._key_swing_modes)
@property
def is_on(self) -> bool:
@@ -180,7 +177,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
@property
def hvac_mode(self):
return self._dict_get_selected(self._key_hvac_modes, "EQUALLY")
return self._dict_get_selected(self._key_hvac_modes)
@property
def hvac_modes(self):
@@ -240,7 +237,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
"""Get on/off status from device attributes."""
if key is None:
return False
value = self.device_attributes.get(key)
value = self._get_nested_value(key)
if isinstance(value, bool):
return value
return value == 1 or value == "on" or value == "true"
@@ -249,34 +246,4 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
"""Set on/off status for device attribute."""
if key is None:
return
await self.async_set_attribute(key, value)
def _dict_get_selected(self, dict_config, rationale="EQUALLY"):
"""Get selected value from dictionary configuration."""
if dict_config is None:
return None
for key, config in dict_config.items():
if isinstance(config, dict):
# Check if all conditions match
match = True
for attr_key, attr_value in config.items():
device_value = self.device_attributes.get(attr_key)
if device_value is None:
match = False
break
if rationale == "EQUALLY":
if device_value != attr_value:
match = False
break
elif rationale == "LESS":
if device_value >= attr_value:
match = False
break
elif rationale == "GREATER":
if device_value <= attr_value:
match = False
break
if match:
return key
return None
await self.async_set_attribute(key, value)

View File

@@ -8,11 +8,14 @@ from homeassistant.core import callback
from homeassistant.const import (
CONF_TYPE,
)
import homeassistant.helpers.config_validation as cv
from .const import (
CONF_ACCOUNT,
CONF_PASSWORD,
DOMAIN,
CONF_SERVER, CONF_SERVERS
CONF_SERVER, CONF_SERVERS,
CONF_HOMES,
CONF_SELECTED_HOMES
)
from .core.cloud import get_midea_cloud
@@ -20,6 +23,8 @@ _LOGGER = logging.getLogger(__name__)
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
_session = None
_cloud = None
_homes = None
@staticmethod
@callback
@@ -41,15 +46,19 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if await cloud.login():
await self.async_set_unique_id(user_input[CONF_ACCOUNT])
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=user_input[CONF_ACCOUNT],
data={
CONF_TYPE: CONF_ACCOUNT,
CONF_ACCOUNT: user_input[CONF_ACCOUNT],
CONF_PASSWORD: user_input[CONF_PASSWORD],
CONF_SERVER: user_input[CONF_SERVER]
},
)
# 保存云实例和用户输入,用于后续步骤
self._cloud = cloud
self._user_input = user_input
# 获取家庭列表
homes = await cloud.list_home()
if homes and len(homes) > 0:
_LOGGER.debug(f"Found homes: {homes}")
self._homes = homes
return await self.async_step_select_homes()
else:
errors["base"] = "no_homes"
else:
errors["base"] = "login_failed"
except Exception as e:
@@ -65,6 +74,55 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors=errors,
)
async def async_step_select_homes(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
"""家庭选择步骤"""
errors: dict[str, str] = {}
if user_input is not None:
selected_homes = user_input.get(CONF_SELECTED_HOMES, [])
if not selected_homes:
errors["base"] = "no_homes_selected"
else:
# 创建配置条目
return self.async_create_entry(
title=self._user_input[CONF_ACCOUNT],
data={
CONF_TYPE: CONF_ACCOUNT,
CONF_ACCOUNT: self._user_input[CONF_ACCOUNT],
CONF_PASSWORD: self._user_input[CONF_PASSWORD],
CONF_SERVER: self._user_input[CONF_SERVER],
CONF_SELECTED_HOMES: selected_homes
},
)
# 构建家庭选择选项
home_options = {}
for home_id, home_info in self._homes.items():
_LOGGER.debug(f"Processing home_id: {home_id}, home_info: {home_info}, type: {type(home_info)}")
# 确保home_id是字符串因为multi_select需要字符串键
home_id_str = str(home_id)
if isinstance(home_info, dict):
home_name = home_info.get("name", f"家庭 {home_id}")
else:
# 如果home_info是字符串直接使用
home_name = str(home_info) if home_info else f"家庭 {home_id}"
home_options[home_id_str] = home_name
# 默认全选
default_selected = list(home_options.keys())
_LOGGER.debug(f"Home options: {home_options}")
_LOGGER.debug(f"Default selected: {default_selected}")
return self.async_show_form(
step_id="select_homes",
data_schema=vol.Schema({
vol.Required(CONF_SELECTED_HOMES, default=default_selected): vol.All(
cv.multi_select(home_options)
)
}),
errors=errors,
)
class OptionsFlowHandler(config_entries.OptionsFlow):
def __init__(self, config_entry: config_entries.ConfigEntry):

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,6 @@
import threading
import socket
import traceback
from enum import IntEnum
from .cloud import MideaCloud
@@ -77,7 +78,7 @@ class MiedaDevice(threading.Thread):
self._centralized = []
self._calculate_get = []
self._calculate_set = []
self._lua_runtime = MideaCodec(lua_file, sn=sn, subtype=subtype) if lua_file is not None else None
self._lua_runtime = MideaCodec(lua_file, device_type=self._attributes.get("device_type"), sn=sn, subtype=subtype) if lua_file is not None else None
self._cloud = cloud
@property
@@ -134,14 +135,49 @@ class MiedaDevice(threading.Thread):
def get_attribute(self, attribute):
return self._attributes.get(attribute)
def _convert_to_nested_structure(self, attributes):
"""Convert dot-notation attributes to nested structure."""
nested = {}
for key, value in attributes.items():
if '.' in key:
# Handle nested attributes with dot notation
keys = key.split('.')
current_dict = nested
# Navigate to the parent dictionary
for k in keys[:-1]:
if k not in current_dict:
current_dict[k] = {}
current_dict = current_dict[k]
# Set the final value
current_dict[keys[-1]] = value
else:
# Handle flat attributes
nested[key] = value
return nested
async def set_attribute(self, attribute, value):
if attribute in self._attributes.keys():
new_status = {}
for attr in self._centralized:
new_status[attr] = self._attributes.get(attr)
new_status[attribute] = value
if set_cmd := self._lua_runtime.build_control(new_status):
await self._build_send(set_cmd)
# Convert dot-notation attributes to nested structure for transmission
nested_status = self._convert_to_nested_structure(new_status)
try:
if set_cmd := self._lua_runtime.build_control(nested_status, status=self._attributes):
await self._build_send(set_cmd)
return
except Exception as e:
MideaLogger.debug(f"LuaRuntimeError in set_attribute {nested_status}: {repr(e)}")
traceback.print_exc()
cloud = self._cloud
if cloud and hasattr(cloud, "send_device_control"):
await cloud.send_device_control(self._device_id, control=nested_status, status=self._attributes)
async def set_attributes(self, attributes):
new_status = {}
@@ -152,9 +188,22 @@ class MiedaDevice(threading.Thread):
if attribute in self._attributes.keys():
has_new = True
new_status[attribute] = value
# Convert dot-notation attributes to nested structure for transmission
nested_status = self._convert_to_nested_structure(new_status)
if has_new:
if set_cmd := self._lua_runtime.build_control(new_status):
await self._build_send(set_cmd)
try:
if set_cmd := self._lua_runtime.build_control(nested_status, status=self._attributes):
await self._build_send(set_cmd)
return
except Exception as e:
MideaLogger.debug(f"LuaRuntimeError in set_attributes {nested_status}: {repr(e)}")
traceback.print_exc()
cloud = self._cloud
if cloud and hasattr(cloud, "send_device_control"):
await cloud.send_device_control(self._device_id, control=nested_status, status=self._attributes)
def set_ip_address(self, ip_address):
MideaLogger.debug(f"Update IP address to {ip_address}")

View File

@@ -1,3 +1,5 @@
import traceback
import lupa
import threading
import json
@@ -6,7 +8,7 @@ from .logger import MideaLogger
class LuaRuntime:
def __init__(self, file):
self._runtimes = lupa.LuaRuntime()
self._runtimes = lupa.lua51.LuaRuntime()
string = f'dofile("{file}")'
self._runtimes.execute(string)
self._lock = threading.Lock()
@@ -26,8 +28,9 @@ class LuaRuntime:
class MideaCodec(LuaRuntime):
def __init__(self, file, sn=None, subtype=None):
def __init__(self, file, device_type=None, sn=None, subtype=None):
super().__init__(file)
self._device_type = device_type
self._sn = sn
self._subtype = subtype
@@ -53,14 +56,28 @@ class MideaCodec(LuaRuntime):
MideaLogger.error(f"LuaRuntimeError in build_query {json_str}: {repr(e)}")
return None
def build_control(self, append=None):
def build_control(self, append=None, status=None):
query_dict = self._build_base_dict()
query_dict["control"] = {} if append is None else append
query_dict["status"] = {} if status is None else status
# 针对T0xD9复式洗衣机特殊处理
if self._device_type == "T0xD9":
control_keys = list(append.keys())
if len(control_keys) > 0:
# 从第一个键名中提取前缀,例如从 'db_power' 中提取 'db'
first_key = control_keys[0]
prefix = first_key.split("_")[0]
query_dict["control"]["bucket"] = prefix
else:
query_dict["control"]["bucket"] = "db"
json_str = json.dumps(query_dict)
MideaLogger.debug(f"LuaRuntime json_str {json_str}")
try:
result = self.json_to_data(json_str)
MideaLogger.debug(f"LuaRuntime Result {result}")
return result
except lupa.LuaError as e:
traceback.print_exc()
MideaLogger.error(f"LuaRuntimeError in build_control {json_str}: {repr(e)}")
return None

File diff suppressed because one or more lines are too long

View File

@@ -6,13 +6,51 @@ from homeassistant.components.switch import SwitchDeviceClass
DEVICE_MAPPING = {
"default": {
"rationale": ["off", "on"],
"queries": [{}],
"centralized": [
"power", "temperature", "small_temperature", "mode", "eco",
"comfort_power_save", "strong_wind",
"wind_swing_lr", "wind_swing_lr", "wind_speed","ptc", "dry"
],
"queries": [{}, {"query_type":"run_status"}],
"centralized": [],
"entities": {
Platform.FAN: {
"fan": {
"power": "new_wind_machine",
"speeds": [
{"fresh_air_fan_speed": 20},
{"fresh_air_fan_speed": 40},
{"fresh_air_fan_speed": 60},
{"fresh_air_fan_speed": 80},
{"fresh_air_fan_speed": 100},
],
"preset_modes": {
"heat_exchange": {
"fresh_air_mode": 1,
"wind_strength": 0
},
"smooth_in": {
"fresh_air_mode": 2,
"wind_strength": 0
},
"rough_in": {
"fresh_air_mode": 2,
"wind_strength": 1
},
"smooth_out": {
"fresh_air_mode": 3,
"wind_strength": 0
},
"rough_out": {
"fresh_air_mode": 3,
"wind_strength": 1
},
"auto": {
"fresh_air_mode": 4,
"wind_strength": 0
},
"innercycle": {
"fresh_air_mode": 5,
"wind_strength": 0
},
}
}
},
Platform.CLIMATE: {
"thermostat": {
"power": "power",
@@ -28,10 +66,11 @@ DEVICE_MAPPING = {
"none": {
"eco": "off",
"comfort_power_save": "off",
"cool_power_saving": 0,
# "comfort_sleep": "off",
"strong_wind": "off"
},
"eco": {"eco": "on"},
"eco": {"eco": "on", "cool_power_saving": 1},
"comfort": {"comfort_power_save": "on"},
# "sleep": {"comfort_sleep": "on"},
"boost": {"strong_wind": "on"}
@@ -60,6 +99,10 @@ DEVICE_MAPPING = {
}
},
Platform.SWITCH: {
"fresh_air_remove_odor": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": [0, 1],
},
"dry": {
"device_class": SwitchDeviceClass.SWITCH,
},
@@ -77,11 +120,11 @@ DEVICE_MAPPING = {
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT
},
"outdoor_temperature": {
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"indoor_humidity": {
"device_class": SensorDeviceClass.HUMIDITY,
"unit_of_measurement": "%",
"state_class": SensorStateClass.MEASUREMENT
},
}
}
}
},
@@ -155,8 +198,12 @@ DEVICE_MAPPING = {
}
},
Platform.SENSOR: {
"indoor_temperature": {
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT
},
"outdoor_temperature": {
"name": "室外机温度",
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT

View File

@@ -127,19 +127,19 @@ DEVICE_MAPPING = {
"state_class": SensorStateClass.MEASUREMENT
},
"people_number": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.DATA_RATE,
"state_class": SensorStateClass.MEASUREMENT
},
"steam_quantity": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"totalstep": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"stepnum": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"hour_set": {
@@ -158,23 +158,23 @@ DEVICE_MAPPING = {
"state_class": SensorStateClass.MEASUREMENT
},
"ota": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"error_code": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"version": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"cbs_version": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"cloudmenuid": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
}
}

View File

@@ -0,0 +1,214 @@
from homeassistant.const import Platform, UnitOfTemperature, PRECISION_HALVES
from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass
from homeassistant.components.switch import SwitchDeviceClass
DEVICE_MAPPING = {
"default": {
"rationale": ["off", "on"],
"queries": [{}, {"query_type":"run_status"}],
"centralized": [],
"entities": {
Platform.CLIMATE: {
"thermostat": {
"power": "power",
"hvac_modes": {
"off": {"power": "off"},
"heat": {"power": "on", "mode.current": "heat"},
"cool": {"power": "on", "mode.current": "cool"},
"dry": {"power": "on", "mode.current": "dry"},
"fan_only": {"power": "on", "mode.current": "fan"}
},
"preset_modes": {
"none": {
"eco.status": "off",
"strong.status": "off",
"sterilize.status": "off",
"selfclean.status": "off",
"humidification.value": "0"
},
"eco": {"eco.status": "on"},
"boost": {"strong.status": "on"},
"sterilize": {"sterilize.status": "on"},
"selfclean": {"selfclean.status": "on"},
"humidify": {"humidification.value": "1"}
},
"swing_modes": {
"off": {"swing.multiple": "false"},
"both": {"swing.multiple": "true"},
"horizontal": {"swing.louver_horizontal.enable": "true"},
"vertical": {"swing.louver_vertical.enable": "true"}
},
"fan_modes": {
"silent": {"wind_speed.level": 1},
"low": {"wind_speed.level": 2},
"medium": {"wind_speed.level": 3},
"high": {"wind_speed.level": 4},
"full": {"wind_speed.level": 5},
"auto": {"wind_speed.level": 6}
},
"target_temperature": "temperature.current",
"current_temperature": "temperature.room",
"min_temp": 17,
"max_temp": 30,
"temperature_unit": UnitOfTemperature.CELSIUS,
"precision": PRECISION_HALVES,
}
},
Platform.SWITCH: {
"eco": {
"device_class": SwitchDeviceClass.SWITCH,
"attribute": "eco.status"
},
"strong": {
"device_class": SwitchDeviceClass.SWITCH,
"attribute": "strong.status"
},
"selfclean": {
"device_class": SwitchDeviceClass.SWITCH,
"attribute": "selfclean.status"
},
"diagnose": {
"device_class": SwitchDeviceClass.SWITCH,
"attribute": "diagnose.status"
},
"idu_silent": {
"device_class": SwitchDeviceClass.SWITCH,
"attribute": "idu_silent.status"
},
"idu_light": {
"device_class": SwitchDeviceClass.SWITCH,
"attribute": "idu_light"
},
"idu_sleep": {
"device_class": SwitchDeviceClass.SWITCH,
"attribute": "idu_sleep.status"
},
"filter_notification": {
"device_class": SwitchDeviceClass.SWITCH,
"attribute": "filter_notification.status"
}
},
Platform.SENSOR: {
"room_temperature": {
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT,
"attribute": "temperature.room"
},
"outside_temperature": {
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT,
"attribute": "temperature.outside"
},
"co2_value": {
"device_class": SensorDeviceClass.CO2,
"unit_of_measurement": "ppm",
"state_class": SensorStateClass.MEASUREMENT,
"attribute": "co2.value"
},
"hcho_value": {
"device_class": SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
"unit_of_measurement": "μg/m³",
"state_class": SensorStateClass.MEASUREMENT,
"attribute": "hcho.value"
},
"pm25_value": {
"device_class": SensorDeviceClass.PM25,
"unit_of_measurement": "μg/m³",
"state_class": SensorStateClass.MEASUREMENT,
"attribute": "pm2_5.value"
},
"wind_speed_level": {
"device_class": SensorDeviceClass.ENUM,
"attribute": "wind_speed.level"
},
"timer_on_timeout": {
"device_class": SensorDeviceClass.DURATION,
"unit_of_measurement": "min",
"state_class": SensorStateClass.MEASUREMENT,
"attribute": "timer.on.timeout"
},
"timer_off_timeout": {
"device_class": SensorDeviceClass.DURATION,
"unit_of_measurement": "min",
"state_class": SensorStateClass.MEASUREMENT,
"attribute": "timer.off.timeout"
},
"selfclean_time_left": {
"device_class": SensorDeviceClass.DURATION,
"unit_of_measurement": "min",
"state_class": SensorStateClass.MEASUREMENT,
"attribute": "selfclean.time_left"
},
"backup_time": {
"device_class": SensorDeviceClass.DURATION,
"unit_of_measurement": "min",
"state_class": SensorStateClass.MEASUREMENT,
"attribute": "backup.time"
},
"cur_fault_code": {
"device_class": SensorDeviceClass.ENUM,
"attribute": "cur_fault.code"
}
},
Platform.SELECT: {
"mode": {
"options": {
"cool": {"mode.current": "cool"},
"dry": {"mode.current": "dry"},
"fan": {"mode.current": "fan"},
"heat": {"mode.current": "heat"}
},
"attribute": "mode.current"
},
"ptc": {
"options": {
"auto": {"ptc.status": "auto"},
"on": {"ptc.status": "on"},
"off": {"ptc.status": "off"},
"separate": {"ptc.status": "separate"}
},
"attribute": "ptc.status"
},
"wind_feeling_mode": {
"options": {
"close": {"wind_feeling.current": "close"},
"soft": {"wind_feeling.current": "soft"}
},
"attribute": "wind_feeling.current"
},
"swing_louver": {
"options": {
"1": {"swing.louver1": "1"},
"2": {"swing.louver1": "2"},
"3": {"swing.louver1": "3"},
"4": {"swing.louver1": "4"},
"5": {"swing.louver1": "5"}
},
"attribute": "swing.louver1"
},
"swing_horizontal": {
"options": {
"1": {"swing.louver_horizontal.level": "1"},
"2": {"swing.louver_horizontal.level": "2"},
"3": {"swing.louver_horizontal.level": "3"},
"4": {"swing.louver_horizontal.level": "4"},
"5": {"swing.louver_horizontal.level": "5"}
},
"attribute": "swing.louver_horizontal.level"
},
"swing_vertical": {
"options": {
"1": {"swing.louver_vertical.level": "1"},
"2": {"swing.louver_vertical.level": "2"},
"3": {"swing.louver_vertical.level": "3"},
"4": {"swing.louver_vertical.level": "4"},
"5": {"swing.louver_vertical.level": "5"}
},
"attribute": "swing.louver_vertical.level"
}
}
}
}
}

View File

@@ -5,7 +5,7 @@ from homeassistant.components.switch import SwitchDeviceClass
DEVICE_MAPPING = {
"default": {
"rationale": [0, 1],
"rationale": ["off", "on"],
"queries": [{}],
"calculate": {
"get": [
@@ -18,110 +18,135 @@ DEVICE_MAPPING = {
}
},
"entities": {
Platform.SWITCH: {
Platform.BINARY_SENSOR: {
"db_power": {
"device_class": SwitchDeviceClass.SWITCH,
"device_class": BinarySensorDeviceClass.RUNNING,
},
},
Platform.SWITCH: {
"db_clean_notification": {
"device_class": SwitchDeviceClass.SWITCH,
},
"db_softener_needed": {
"device_class": SwitchDeviceClass.SWITCH,
},
"db_detergent_needed": {
"device_class": SwitchDeviceClass.SWITCH,
},
"db_nightly_wash": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": [0, 1],
},
"db_baby_lock": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": [0, 1],
},
"db_light": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": [0, 1],
},
"db_steam_wash": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": [0, 1],
},
"db_fast_clean_wash": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": [0, 1],
},
"db_wash_dry_link": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": [0, 1],
}
},
Platform.SELECT: {
"db_running_status": {
"options": {
"stop": {"db_running_status": "stop"},
"start": {"db_running_status": "start"},
"pause": {"db_running_status": "pause"},
"finish": {"db_running_status": "finish"},
"error": {"db_running_status": "error"}
"off": {"db_power": "off", "db_running_status": "off"},
"standby": {"db_power": "on", "db_running_status": "standby"},
"start": {"db_power": "on", "db_running_status": "start"},
"pause": {"db_power": "on", "db_running_status": "pause"},
"end": {"db_power": "on", "db_running_status": "end"},
"fault": {"db_power": "on", "db_running_status": "fault"},
"delay": {"db_power": "on", "db_running_status": "delay"}
}
},
"db_program": {
"options": {
"fast_wash_30": {"db_program": "fast_wash_30"},
"normal_wash": {"db_program": "normal_wash"},
"heavy_wash": {"db_program": "heavy_wash"},
"delicate_wash": {"db_program": "delicate_wash"},
"cotton": {"db_program": "cotton"},
"eco": {"db_program": "eco"},
"fast_wash": {"db_program": "fast_wash"},
"mixed_wash": {"db_program": "mixed_wash"},
"wool": {"db_program": "wool"},
"ssp": {"db_program": "ssp"},
"sport_clothes": {"db_program": "sport_clothes"},
"single_dehytration": {"db_program": "single_dehytration"},
"rinsing_dehydration": {"db_program": "rinsing_dehydration"},
"big": {"db_program": "big"},
"baby_clothes": {"db_program": "baby_clothes"},
"down_jacket": {"db_program": "down_jacket"},
"color": {"db_program": "color"},
"intelligent": {"db_program": "intelligent"},
"quick_wash": {"db_program": "quick_wash"},
"eco_wash": {"db_program": "eco_wash"}
}
},
"db_water_level": {
"options": {
"low": {"db_water_level": "1"},
"medium": {"db_water_level": "2"},
"high": {"db_water_level": "3"},
"extra_high": {"db_water_level": "4"}
}
},
"db_temperature": {
"options": {
"cold": {"db_temperature": "1"},
"warm": {"db_temperature": "2"},
"hot": {"db_temperature": "3"},
"extra_hot": {"db_temperature": "4"}
}
},
"dehydration_speed": {
"options": {
"low": {"dehydration_speed": "1"},
"medium": {"dehydration_speed": "2"},
"high": {"dehydration_speed": "3"},
"extra_high": {"dehydration_speed": "4"}
}
},
"db_detergent": {
"options": {
"none": {"db_detergent": "1"},
"little": {"db_detergent": "2"},
"normal": {"db_detergent": "3"},
"more": {"db_detergent": "4"}
}
},
"db_softener": {
"options": {
"none": {"db_softener": "1"},
"little": {"db_softener": "2"},
"normal": {"db_softener": "3"},
"more": {"db_softener": "4"}
}
},
"db_position": {
"options": {
"position_1": {"db_position": "1"},
"position_2": {"db_position": "2"},
"position_3": {"db_position": "3"}
}
},
"db_location": {
"options": {
"location_1": {"db_location": "1"},
"location_2": {"db_location": "2"},
"location_3": {"db_location": "3"}
"shirt": {"db_program": "shirt"},
"fiber": {"db_program": "fiber"},
"enzyme": {"db_program": "enzyme"},
"underwear": {"db_program": "underwear"},
"outdoor": {"db_program": "outdoor"},
"air_wash": {"db_program": "air_wash"},
"single_drying": {"db_program": "single_drying"},
"steep": {"db_program": "steep"},
"kids": {"db_program": "kids"},
"water_baby_clothes": {"db_program": "water_baby_clothes"},
"fast_wash_30": {"db_program": "fast_wash_30"},
"water_shirt": {"db_program": "water_shirt"},
"water_mixed_wash": {"db_program": "water_mixed_wash"},
"water_fiber": {"db_program": "water_fiber"},
"water_kids": {"db_program": "water_kids"},
"water_underwear": {"db_program": "water_underwear"},
"specialist": {"db_program": "specialist"},
"love": {"db_program": "love"},
"water_intelligent": {"db_program": "water_intelligent"},
"water_steep": {"db_program": "water_steep"},
"water_fast_wash_30": {"db_program": "water_fast_wash_30"},
"new_water_cotton": {"db_program": "new_water_cotton"},
"water_eco": {"db_program": "water_eco"},
"wash_drying_60": {"db_program": "wash_drying_60"},
"self_wash_5": {"db_program": "self_wash_5"},
"fast_wash_min": {"db_program": "fast_wash_min"},
"mixed_wash_min": {"db_program": "mixed_wash_min"},
"dehydration_min": {"db_program": "dehydration_min"},
"self_wash_min": {"db_program": "self_wash_min"},
"baby_clothes_min": {"db_program": "baby_clothes_min"},
"silk_wash": {"db_program": "silk_wash"},
"prevent_allergy": {"db_program": "prevent_allergy"},
"cold_wash": {"db_program": "cold_wash"},
"soft_wash": {"db_program": "soft_wash"},
"remove_mite_wash": {"db_program": "remove_mite_wash"},
"water_intense_wash": {"db_program": "water_intense_wash"},
"fast_dry": {"db_program": "fast_dry"},
"water_outdoor": {"db_program": "water_outdoor"},
"spring_autumn_wash": {"db_program": "spring_autumn_wash"},
"summer_wash": {"db_program": "summer_wash"},
"winter_wash": {"db_program": "winter_wash"},
"jean": {"db_program": "jean"},
"new_clothes_wash": {"db_program": "new_clothes_wash"},
"silk": {"db_program": "silk"},
"insight_wash": {"db_program": "insight_wash"},
"fitness_clothes": {"db_program": "fitness_clothes"},
"mink": {"db_program": "mink"},
"fresh_air": {"db_program": "fresh_air"},
"bucket_dry": {"db_program": "bucket_dry"},
"jacket": {"db_program": "jacket"},
"bath_towel": {"db_program": "bath_towel"},
"night_fresh_wash": {"db_program": "night_fresh_wash"},
"degerm": {"db_program": "degerm"},
"heart_wash": {"db_program": "heart_wash"},
"water_cold_wash": {"db_program": "water_cold_wash"},
"water_prevent_allergy": {"db_program": "water_prevent_allergy"},
"water_remove_mite_wash": {"db_program": "water_remove_mite_wash"},
"water_ssp": {"db_program": "water_ssp"},
"standard": {"db_program": "standard"},
"green_wool": {"db_program": "green_wool"},
"cook_wash": {"db_program": "cook_wash"},
"fresh_remove_wrinkle": {"db_program": "fresh_remove_wrinkle"},
"steam_sterilize_wash": {"db_program": "steam_sterilize_wash"},
"sterilize_wash": {"db_program": "sterilize_wash"},
"white_clothes_clean": {"db_program": "white_clothes_clean"},
"clean_stains": {"db_program": "clean_stains"},
"prevent_cross_color": {"db_program": "prevent_cross_color"},
"quick_dry_clothes": {"db_program": "quick_dry_clothes"},
"yoga_clothes": {"db_program": "yoga_clothes"}
}
}
},
@@ -149,9 +174,6 @@ DEVICE_MAPPING = {
"unit_of_measurement": UnitOfTime.MINUTES,
"state_class": SensorStateClass.MEASUREMENT
},
"db_device_software_version": {
"device_class": SensorDeviceClass.ENUM
},
"db_rinse_count": {
"device_class": SensorDeviceClass.ENUM
},

View File

@@ -5,7 +5,7 @@ from homeassistant.components.switch import SwitchDeviceClass
DEVICE_MAPPING = {
"default": {
"rationale": [0, 1],
"rationale": ["off", "on"],
"queries": [{}],
"calculate": {
"get": [
@@ -18,10 +18,12 @@ DEVICE_MAPPING = {
}
},
"entities": {
Platform.SWITCH: {
Platform.BINARY_SENSOR: {
"power": {
"device_class": SwitchDeviceClass.SWITCH,
"device_class": BinarySensorDeviceClass.RUNNING,
},
},
Platform.SWITCH: {
"softener_lack": {
"device_class": SwitchDeviceClass.SWITCH,
},
@@ -66,6 +68,7 @@ DEVICE_MAPPING = {
},
"silent": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": ["0", "1"],
},
"speedy": {
"device_class": SwitchDeviceClass.SWITCH,
@@ -108,151 +111,218 @@ DEVICE_MAPPING = {
},
"ultraviolet_lamp": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": ["0", "1"],
},
"eye_wash": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": ["0", "1"],
},
"microbubble": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": ["0", "1"],
},
"wind_dispel": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": ["0", "1"],
},
"cycle_memory": {
"device_class": SwitchDeviceClass.SWITCH,
}
},
"disinfectant": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": ["0", "1"],
},
"add_rinse": {
"device_class": SwitchDeviceClass.SWITCH,
"rationale": ["0", "1"],
},
},
Platform.SELECT: {
"running_status": {
"options": {
"standby": {"running_status": "standby"},
"running": {"running_status": "running"},
"pause": {"running_status": "pause"},
"finish": {"running_status": "finish"},
"error": {"running_status": "error"}
"off": {"power": "off", "running_status": "off"},
"standby": {"power": "on", "running_status": "standby"},
"start": {"power": "on", "running_status": "start"},
"pause": {"power": "on", "running_status": "pause"},
"end": {"power": "on", "running_status": "end"},
"fault": {"power": "on", "running_status": "fault"},
"delay": {"power": "on", "running_status": "delay"}
}
},
"db_dehydration_speed": {
"dehydration_speed": {
"options": {
"low": {"db_dehydration_speed": "1"},
"medium": {"db_dehydration_speed": "2"},
"high": {"db_dehydration_speed": "3"},
"extra_high": {"db_dehydration_speed": "4"}
"0": {"dehydration_speed": "0"},
"400": {"dehydration_speed": "400"},
"600": {"dehydration_speed": "600"},
"800": {"dehydration_speed": "800"},
"1000": {"dehydration_speed": "1000"},
"1200": {"dehydration_speed": "1200"},
"1400": {"dehydration_speed": "1400"},
"1600": {"dehydration_speed": "1600"},
"1300": {"dehydration_speed": "1300"}
}
},
"mode": {
"options": {
"normal": {"mode": "normal"},
"eco": {"mode": "eco"},
"quick": {"mode": "quick"},
"heavy": {"mode": "heavy"},
"delicate": {"mode": "delicate"}
"factory_test": {"mode": "factory_test"},
"service": {"mode": "service"},
"normal_continus": {"mode": "normal_continus"}
}
},
"water_level": {
"options": {
"low": {"water_level": "low"},
"medium": {"water_level": "medium"},
"medium": {"water_level": "mid"},
"high": {"water_level": "high"},
"extra_high": {"water_level": "extra_high"}
"auto": {"water_level": "auto"}
}
},
"program": {
"options": {
"ssp": {"program": "ssp"},
"cotton": {"program": "cotton"},
"synthetic": {"program": "synthetic"},
"eco": {"program": "eco"},
"fast_wash": {"program": "fast_wash"},
"mixed_wash": {"program": "mixed_wash"},
"wool": {"program": "wool"},
"delicate": {"program": "delicate"},
"quick": {"program": "quick"}
"ssp": {"program": "ssp"},
"sport_clothes": {"program": "sport_clothes"},
"single_dehytration": {"program": "single_dehytration"},
"rinsing_dehydration": {"program": "rinsing_dehydration"},
"big": {"program": "big"},
"baby_clothes": {"program": "baby_clothes"},
"down_jacket": {"program": "down_jacket"},
"color": {"program": "color"},
"intelligent": {"program": "intelligent"},
"quick_wash": {"program": "quick_wash"},
"shirt": {"program": "shirt"},
"fiber": {"program": "fiber"},
"enzyme": {"program": "enzyme"},
"underwear": {"program": "underwear"},
"outdoor": {"program": "outdoor"},
"air_wash": {"program": "air_wash"},
"single_drying": {"program": "single_drying"},
"steep": {"program": "steep"},
"kids": {"program": "kids"},
"water_cotton": {"program": "water_cotton"},
"fast_wash_30": {"program": "fast_wash_30"},
"fast_wash_60": {"program": "fast_wash_60"},
"water_mixed_wash": {"program": "water_mixed_wash"},
"water_fiber": {"program": "water_fiber"},
"water_kids": {"program": "water_kids"},
"water_underwear": {"program": "water_underwear"},
"specialist": {"program": "specialist"},
"love": {"program": "love"},
"water_intelligent": {"program": "water_intelligent"},
"water_steep": {"program": "water_steep"},
"water_fast_wash_30": {"program": "water_fast_wash_30"},
"new_water_cotton": {"program": "new_water_cotton"},
"water_eco": {"program": "water_eco"},
"wash_drying_60": {"program": "wash_drying_60"},
"self_wash_5": {"program": "self_wash_5"},
"fast_wash_min": {"program": "fast_wash_min"},
"mixed_wash_min": {"program": "mixed_wash_min"},
"dehydration_min": {"program": "dehydration_min"},
"self_wash_min": {"program": "self_wash_min"},
"baby_clothes_min": {"program": "baby_clothes_min"},
"diy0": {"program": "diy0"},
"diy1": {"program": "diy1"},
"diy2": {"program": "diy2"},
"silk_wash": {"program": "silk_wash"},
"prevent_allergy": {"program": "prevent_allergy"},
"cold_wash": {"program": "cold_wash"},
"soft_wash": {"program": "soft_wash"},
"remove_mite_wash": {"program": "remove_mite_wash"},
"water_intense_wash": {"program": "water_intense_wash"},
"fast_dry": {"program": "fast_dry"},
"water_outdoor": {"program": "water_outdoor"},
"spring_autumn_wash": {"program": "spring_autumn_wash"},
"summer_wash": {"program": "summer_wash"},
"winter_wash": {"program": "winter_wash"},
"jean": {"program": "jean"},
"new_clothes_wash": {"program": "new_clothes_wash"},
"silk": {"program": "silk"},
"insight_wash": {"program": "insight_wash"},
"fitness_clothes": {"program": "fitness_clothes"},
"mink": {"program": "mink"},
"fresh_air": {"program": "fresh_air"},
"bucket_dry": {"program": "bucket_dry"},
"jacket": {"program": "jacket"},
"bath_towel": {"program": "bath_towel"},
"night_fresh_wash": {"program": "night_fresh_wash"},
"heart_wash": {"program": "heart_wash"},
"water_cold_wash": {"program": "water_cold_wash"},
"water_prevent_allergy": {"program": "water_prevent_allergy"},
"water_remove_mite_wash": {"program": "water_remove_mite_wash"},
"water_ssp": {"program": "water_ssp"},
"standard": {"program": "standard"},
"green_wool": {"program": "green_wool"},
"cook_wash": {"program": "cook_wash"},
"fresh_remove_wrinkle": {"program": "fresh_remove_wrinkle"},
"steam_sterilize_wash": {"program": "steam_sterilize_wash"},
"aromatherapy": {"program": "aromatherapy"},
"sterilize_wash": {"program": "sterilize_wash"},
"white_clothes_clean": {"program": "white_clothes_clean"},
"clean_stains": {"program": "clean_stains"},
"tube_clean_all": {"program": "tube_clean_all"},
"no_channeling_color": {"program": "no_channeling_color"},
"scald_wash": {"program": "scald_wash"},
"hanfu_spring_summer": {"program": "hanfu_spring_summer"},
"hanfu_autumn_winter": {"program": "hanfu_autumn_winter"},
"skin_care_wash": {"program": "skin_care_wash"},
"hanfu_wash": {"program": "hanfu_wash"}
}
},
"temperature": {
"options": {
"cold": {"temperature": "cold"},
"warm": {"temperature": "warm"},
"hot": {"temperature": "hot"},
"extra_hot": {"temperature": "extra_hot"}
}
},
"detergent_density": {
"options": {
"low": {"detergent_density": "low"},
"medium": {"detergent_density": "medium"},
"high": {"detergent_density": "high"},
"extra_high": {"detergent_density": "extra_high"}
}
},
"softener_density": {
"options": {
"low": {"softener_density": "low"},
"medium": {"softener_density": "medium"},
"high": {"softener_density": "high"},
"extra_high": {"softener_density": "extra_high"}
"0": {"temperature": "0"},
"20": {"temperature": "20"},
"30": {"temperature": "30"},
"40": {"temperature": "40"},
"50": {"temperature": "50"},
"60": {"temperature": "60"},
"70": {"temperature": "70"},
"90": {"temperature": "90"},
"95": {"temperature": "95"}
}
},
"detergent": {
"options": {
"none": {"detergent": "none"},
"little": {"detergent": "little"},
"normal": {"detergent": "normal"},
"more": {"detergent": "more"}
"0": {"detergent": "0"},
"1": {"detergent": "1"},
"2": {"detergent": "2"},
"3": {"detergent": "3"},
"4": {"detergent": "4"},
"5": {"detergent": "5"}
}
},
"softener": {
"options": {
"none": {"softener": "none"},
"little": {"softener": "little"},
"normal": {"softener": "normal"},
"more": {"softener": "more"}
}
},
"season": {
"options": {
"spring": {"season": "spring"},
"summer": {"season": "summer"},
"autumn": {"season": "autumn"},
"winter": {"season": "winter"}
}
},
"disinfectant": {
"options": {
"none": {"disinfectant": "none"},
"light": {"disinfectant": "light"},
"medium": {"disinfectant": "medium"},
"strong": {"disinfectant": "strong"}
"0": {"softener": "0"},
"1": {"softener": "1"},
"2": {"softener": "2"},
"3": {"softener": "3"},
"4": {"softener": "4"},
"5": {"softener": "5"}
}
},
"dirty_degree": {
"options": {
"light": {"dirty_degree": "light"},
"medium": {"dirty_degree": "medium"},
"heavy": {"dirty_degree": "heavy"},
"extra_heavy": {"dirty_degree": "extra_heavy"}
}
},
"stains": {
"options": {
"none": {"stains": "none"},
"light": {"stains": "light"},
"medium": {"stains": "medium"},
"heavy": {"stains": "heavy"}
}
},
"add_rinse": {
"options": {
"none": {"add_rinse": "none"},
"one": {"add_rinse": "one"},
"two": {"add_rinse": "two"},
"three": {"add_rinse": "three"}
"0": {"dirty_degree": "0"},
"1": {"dirty_degree": "1"},
"2": {"dirty_degree": "2"},
"3": {"dirty_degree": "3"},
"4": {"dirty_degree": "4"}
}
},
"soak_count": {
"options": {
"none": {"soak_count": "none"},
"one": {"soak_count": "one"},
"two": {"soak_count": "two"},
"three": {"soak_count": "three"}
"0": {"soak_count": "0"},
"1": {"soak_count": "1"},
"2": {"soak_count": "2"},
"3": {"soak_count": "3"},
"4": {"soak_count": "4"},
"5": {"soak_count": "5"}
}
}
},

View File

@@ -65,6 +65,31 @@ DEVICE_MAPPING = {
"power_on": {"work_status": "power_on" },
}
},
"wash_mode": {
"options": {
"neutral_gear": {"mode": "neutral_gear"},
"auto_wash": {"mode": "auto_wash"},
"strong_wash": {"mode": "strong_wash"},
"standard_wash": {"mode": "standard_wash"},
"eco_wash": {"mode": "eco_wash"},
"glass_wash": {"mode": "glass_wash"},
"hour_wash": {"mode": "hour_wash"},
"fast_wash": {"mode": "fast_wash"},
"soak_wash": {"mode": "soak_wash"},
"90min_wash": {"mode": "90min_wash"},
"self_clean": {"mode": "self_clean"},
"fruit_wash": {"mode": "fruit_wash"},
"self_define": {"mode": "self_define"},
"germ": {"mode": "germ"},
"bowl_wash": {"mode": "bowl_wash"},
"kill_germ": {"mode": "kill_germ"},
"seafood_wash": {"mode": "seafood_wash"},
"hotpot_wash": {"mode": "hotpot_wash"},
"quietnight_wash": {"mode": "quietnight_wash"},
"less_wash": {"mode": "less_wash"},
"oilnet_wash": {"mode": "oilnet_wash"}
}
},
},
Platform.SENSOR: {
"bright": {

View File

@@ -8,38 +8,14 @@ DEVICE_MAPPING = {
"manufacturer": "美的",
"rationale": ["off", "on"],
"queries": [{}],
"centralized": [
"power", "temperature", "mode", "heat", "music", "ti_protect", "fast_wash",
"ali_manager", "water_quality", "rate", "ele_exception", "communication_error",
"cur_rate", "sterilize_left_days", "uv_sterilize_minute", "uv_sterilize_second",
"eplus", "summer", "winter", "efficient", "night", "bath_person", "cloud",
"bath", "half_heat", "whole_heat", "sterilization", "frequency_hot", "scene",
"big_water", "wash", "negative_ions", "screen_off", "t_hot", "baby_wash",
"dad_wash", "mom_wash", "wash_with_temp", "single_wash", "people_wash",
"wash_temperature", "one_egg", "two_egg", "always_fell", "smart_sterilize",
"sterilize_cycle_index", "sound_dad", "screen_light", "morning_night_bash",
"version", "tds_value", "door_status", "limit_error", "sensor_error",
"scene_id", "auto_off", "clean", "volume", "passwater_lowbyte", "cloud_appoint",
"protect", "midea_manager", "sleep", "memory", "shower", "scroll_hot",
"fast_hot_power", "hot_power", "safe", "water_flow", "heat_water_level",
"flow", "appoint_wash", "now_wash", "end_time_hour", "end_time_minute",
"get_time", "get_temp", "func_select", "warm_power", "type_select",
"cur_temperature", "sterilize_high_temp", "discharge_status", "top_temp",
"bottom_heat", "top_heat", "show_h", "uv_sterilize", "machine", "error_code",
"need_discharge", "elec_warning", "bottom_temp", "water_cyclic", "water_system",
"discharge_left_time", "in_temperature", "mg_remain", "waterday_lowbyte",
"waterday_highbyte", "tech_water", "protect_show", "appoint_power"
],
"centralized": [],
"entities": {
Platform.WATER_HEATER: {
"water_heater": {
"power": "power",
"operation_list": {
"off": {"power": "off"},
"heat": {"power": "on", "mode": "heat"},
"auto": {"power": "on", "mode": "auto"},
"eco": {"power": "on", "mode": "eco"},
"fast": {"power": "on", "mode": "fast"}
"on": {"power": "on"},
},
"target_temperature": "temperature",
"current_temperature": "cur_temperature",
@@ -50,221 +26,29 @@ DEVICE_MAPPING = {
}
},
Platform.SWITCH: {
"music": {
"device_class": SwitchDeviceClass.SWITCH,
},
"ti_protect": {
"device_class": SwitchDeviceClass.SWITCH,
},
"fast_wash": {
"device_class": SwitchDeviceClass.SWITCH,
},
"ali_manager": {
"device_class": SwitchDeviceClass.SWITCH,
},
"heat": {
"device_class": SwitchDeviceClass.SWITCH,
},
"ele_exception": {
"device_class": SwitchDeviceClass.SWITCH,
},
"communication_error": {
"device_class": SwitchDeviceClass.SWITCH,
},
"eplus": {
"device_class": SwitchDeviceClass.SWITCH,
},
"summer": {
"device_class": SwitchDeviceClass.SWITCH,
},
"winter": {
"device_class": SwitchDeviceClass.SWITCH,
},
"efficient": {
"device_class": SwitchDeviceClass.SWITCH,
},
"night": {
"device_class": SwitchDeviceClass.SWITCH,
},
"bath_person": {
"device_class": SwitchDeviceClass.SWITCH,
},
"cloud": {
"device_class": SwitchDeviceClass.SWITCH,
},
"bath": {
"device_class": SwitchDeviceClass.SWITCH,
},
"half_heat": {
"device_class": SwitchDeviceClass.SWITCH,
},
"whole_heat": {
"device_class": SwitchDeviceClass.SWITCH,
},
"sterilization": {
"device_class": SwitchDeviceClass.SWITCH,
},
"frequency_hot": {
"device_class": SwitchDeviceClass.SWITCH,
},
"scene": {
"device_class": SwitchDeviceClass.SWITCH,
},
"big_water": {
"device_class": SwitchDeviceClass.SWITCH,
},
"wash": {
"device_class": SwitchDeviceClass.SWITCH,
},
"negative_ions": {
"device_class": SwitchDeviceClass.SWITCH,
},
"screen_off": {
"device_class": SwitchDeviceClass.SWITCH,
},
"t_hot": {
"device_class": SwitchDeviceClass.SWITCH,
},
"baby_wash": {
"device_class": SwitchDeviceClass.SWITCH,
},
"dad_wash": {
"device_class": SwitchDeviceClass.SWITCH,
},
"mom_wash": {
"device_class": SwitchDeviceClass.SWITCH,
},
"wash_with_temp": {
"device_class": SwitchDeviceClass.SWITCH,
},
"single_wash": {
"device_class": SwitchDeviceClass.SWITCH,
},
"people_wash": {
"device_class": SwitchDeviceClass.SWITCH,
},
"one_egg": {
"device_class": SwitchDeviceClass.SWITCH,
},
"two_egg": {
"device_class": SwitchDeviceClass.SWITCH,
},
"always_fell": {
"device_class": SwitchDeviceClass.SWITCH,
},
"smart_sterilize": {
"device_class": SwitchDeviceClass.SWITCH,
},
"sound_dad": {
"device_class": SwitchDeviceClass.SWITCH,
},
"door_status": {
"device_class": SwitchDeviceClass.SWITCH,
},
"limit_error": {
"device_class": SwitchDeviceClass.SWITCH,
},
"sensor_error": {
"device_class": SwitchDeviceClass.SWITCH,
},
"auto_off": {
"device_class": SwitchDeviceClass.SWITCH,
},
"clean": {
"device_class": SwitchDeviceClass.SWITCH,
},
"cloud_appoint": {
"device_class": SwitchDeviceClass.SWITCH,
},
"protect": {
"device_class": SwitchDeviceClass.SWITCH,
},
"midea_manager": {
"device_class": SwitchDeviceClass.SWITCH,
},
"sleep": {
"device_class": SwitchDeviceClass.SWITCH,
},
"memory": {
"device_class": SwitchDeviceClass.SWITCH,
},
"shower": {
"device_class": SwitchDeviceClass.SWITCH,
},
"scroll_hot": {
"device_class": SwitchDeviceClass.SWITCH,
},
"fast_hot_power": {
"device_class": SwitchDeviceClass.SWITCH,
},
"hot_power": {
"device_class": SwitchDeviceClass.SWITCH,
},
"safe": {
"device_class": SwitchDeviceClass.SWITCH,
},
"water_flow": {
"device_class": SwitchDeviceClass.SWITCH,
},
"appoint_wash": {
"device_class": SwitchDeviceClass.SWITCH,
},
"now_wash": {
"device_class": SwitchDeviceClass.SWITCH,
},
"get_time": {
"device_class": SwitchDeviceClass.SWITCH,
},
"get_temp": {
"device_class": SwitchDeviceClass.SWITCH,
},
"warm_power": {
"device_class": SwitchDeviceClass.SWITCH,
},
"sterilize_high_temp": {
"device_class": SwitchDeviceClass.SWITCH,
},
"bottom_heat": {
"device_class": SwitchDeviceClass.SWITCH,
},
"top_heat": {
"device_class": SwitchDeviceClass.SWITCH,
},
"show_h": {
"device_class": SwitchDeviceClass.SWITCH,
},
"uv_sterilize": {
"device_class": SwitchDeviceClass.SWITCH,
},
"need_discharge": {
"device_class": SwitchDeviceClass.SWITCH,
},
"elec_warning": {
"device_class": SwitchDeviceClass.SWITCH,
},
"water_cyclic": {
"device_class": SwitchDeviceClass.SWITCH,
},
"tech_water": {
"device_class": SwitchDeviceClass.SWITCH,
},
"protect_show": {
"device_class": SwitchDeviceClass.SWITCH,
},
"appoint_power": {
"device_class": SwitchDeviceClass.SWITCH,
}
},
Platform.SELECT: {
"mode": {
"options": {
"none": {"mode": "none"},
"heat": {"mode": "heat"},
"auto": {"mode": "auto"},
"eco": {"mode": "eco"},
"fast": {"mode": "fast"}
}
},
"water_quality": {
"options": {
"0": {"water_quality": 0},
@@ -276,15 +60,13 @@ DEVICE_MAPPING = {
"func_select": {
"options": {
"low": {"func_select": "low"},
"medium": {"func_select": "medium"},
"high": {"func_select": "high"}
"medium": {"func_select": "medium"}
}
},
"type_select": {
"options": {
"normal": {"type_select": "normal"},
"eco": {"type_select": "eco"},
"fast": {"type_select": "fast"}
"valve": {"type_select": "valve"},
}
},
"machine": {
@@ -365,22 +147,11 @@ DEVICE_MAPPING = {
"unit_of_measurement": UnitOfTime.MINUTES,
"state_class": SensorStateClass.MEASUREMENT
},
"version": {
"device_class": SensorDeviceClass.ENUM
},
"tds_value": {
"device_class": SensorDeviceClass.WATER,
"unit_of_measurement": "ppm",
"state_class": SensorStateClass.MEASUREMENT
},
"scene_id": {
"device_class": SensorDeviceClass.ENUM
},
"volume": {
"device_class": SensorDeviceClass.SOUND_PRESSURE,
"unit_of_measurement": "%",
"state_class": SensorStateClass.MEASUREMENT
},
"heat_water_level": {
"device_class": SensorDeviceClass.WATER,
"unit_of_measurement": "%",

View File

@@ -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
}
}
}
}

View File

@@ -107,7 +107,7 @@ DEVICE_MAPPING = {
},
Platform.SENSOR: {
"real_gear": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"dust_life_time": {
@@ -130,7 +130,7 @@ DEVICE_MAPPING = {
"state_class": SensorStateClass.MEASUREMENT
},
"error_code": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"temperature_feedback": {
@@ -139,7 +139,7 @@ DEVICE_MAPPING = {
"state_class": SensorStateClass.MEASUREMENT
},
"water_feedback": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"timer_off_hour": {
@@ -163,7 +163,7 @@ DEVICE_MAPPING = {
"state_class": SensorStateClass.MEASUREMENT
},
"version": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"pm25": {
@@ -172,26 +172,26 @@ DEVICE_MAPPING = {
"state_class": SensorStateClass.MEASUREMENT
},
"ud_swing_angle": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"state_class": SensorStateClass.MEASUREMENT
},
"lr_diy_down_percent": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"unit_of_measurement": PERCENTAGE,
"state_class": SensorStateClass.MEASUREMENT
},
"lr_diy_up_percent": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"unit_of_measurement": PERCENTAGE,
"state_class": SensorStateClass.MEASUREMENT
},
"ud_diy_down_percent": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"unit_of_measurement": PERCENTAGE,
"state_class": SensorStateClass.MEASUREMENT
},
"ud_diy_up_percent": {
"device_class": SensorDeviceClass.NONE,
"device_class": SensorDeviceClass.ENUM,
"unit_of_measurement": PERCENTAGE,
"state_class": SensorStateClass.MEASUREMENT
}

View File

@@ -1,11 +1,19 @@
from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
from .midea_entity import MideaEntity
from . import load_device_config
async def async_setup_entry(hass, config_entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
if not account_bucket:
async_add_entities([])
@@ -44,10 +52,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")
@@ -57,7 +61,9 @@ class MideaFanEntity(MideaEntity, FanEntity):
@property
def supported_features(self):
features = 0
features = FanEntityFeature(0)
features |= FanEntityFeature.TURN_ON
features |= FanEntityFeature.TURN_OFF
if self._key_preset_modes is not None and len(self._key_preset_modes) > 0:
features |= FanEntityFeature.PRESET_MODE
if self._key_speeds is not None and len(self._key_speeds) > 0:
@@ -104,8 +110,7 @@ class MideaFanEntity(MideaEntity, FanEntity):
index = round(percentage * self._attr_speed_count / 100) - 1
index = max(0, min(index, len(self._key_speeds) - 1))
new_status.update(self._key_speeds[index])
if self._key_power is not None:
new_status[self._key_power] = True
await self._async_set_status_on_off(self._key_power, True)
if new_status:
await self.async_set_attributes(new_status)
@@ -131,8 +136,3 @@ class MideaFanEntity(MideaEntity, FanEntity):
if self.oscillating != oscillating:
await self._async_set_status_on_off(self._key_oscillate, oscillating)
def update_state(self, status):
try:
self.schedule_update_ha_state()
except Exception:
pass

View File

@@ -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):

View File

@@ -7,5 +7,5 @@
"iot_class": "cloud_push",
"issue_tracker": "https://github.com/sususweet/midea-meiju-codec/issues",
"requirements": ["lupa>=2.0"],
"version": "v0.1.0"
"version": "v0.1.3"
}

View File

@@ -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,
@@ -135,83 +141,122 @@ class MideaEntity(CoordinatorEntity[MideaDataUpdateCoordinator], Entity):
pass
# ===== Unified helpers migrated from legacy entity base =====
def _get_nested_value(self, attribute_key: str | None) -> Any:
"""Get nested value from device attributes using dot notation.
Supports both flat and nested attribute access.
Examples: 'power', 'eco.status', 'temperature.room'
"""
if attribute_key is None:
return None
# Handle nested attributes with dot notation
if '.' in attribute_key:
keys = attribute_key.split('.')
value = self.device_attributes
try:
for key in keys:
if isinstance(value, dict):
value = value.get(key)
else:
return None
return value
except (KeyError, TypeError):
return None
else:
# Handle flat attributes
return self.device_attributes.get(attribute_key)
def _get_status_on_off(self, attribute_key: str | None) -> bool:
"""Return boolean value from device attributes for given key.
Accepts common truthy representations: True/1/"on"/"true".
Supports nested attributes with dot notation.
"""
result = False
if attribute_key is None:
return result
status = self._get_nested_value(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
def _set_nested_value(self, attribute_key: str, value: Any) -> None:
"""Set nested value in device attributes using dot notation.
Supports both flat and nested attribute setting.
Examples: 'power', 'eco.status', 'temperature.room'
"""
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
# Handle nested attributes with dot notation
if '.' in attribute_key:
keys = attribute_key.split('.')
current_dict = self.device_attributes
# Navigate to the parent dictionary
for key in keys[:-1]:
if key not in current_dict:
current_dict[key] = {}
current_dict = current_dict[key]
# Set the final value
current_dict[keys[-1]] = value
else:
# Handle flat attributes
self.device_attributes[attribute_key] = value
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))
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._get_nested_value(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._get_nested_value(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:

View File

@@ -1,11 +1,19 @@
from homeassistant.components.select import SelectEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
from .midea_entity import MideaEntity
from . import load_device_config
async def async_setup_entry(hass, config_entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
if not account_bucket:
async_add_entities([])
@@ -44,10 +52,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
@@ -56,7 +60,14 @@ class MideaSelectEntity(MideaEntity, SelectEntity):
@property
def current_option(self):
return self._dict_get_selected(self._key_options)
# Use attribute from config if available, otherwise fall back to entity_key
attribute = self._config.get("attribute", self._entity_key)
if attribute and attribute != self._entity_key:
# For simple attribute access, get the value directly
return self._get_nested_value(attribute)
else:
# For complex mapping, use the existing logic
return self._dict_get_selected(self._key_options)
async def async_select_option(self, option: str):
new_status = self._key_options.get(option)

View File

@@ -5,7 +5,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
@@ -58,15 +57,13 @@ 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):
"""Return the native value of the sensor."""
value = self.device_attributes.get(self._entity_key)
# 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)
# Handle invalid string values
if isinstance(value, str) and value.lower() in ['invalid', 'none', 'null', '']:

View File

@@ -5,7 +5,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
@@ -58,24 +57,22 @@ 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"
# Use attribute from config if available, otherwise fall back to entity_key
attribute = self._config.get("attribute", self._entity_key)
return self._get_status_on_off(attribute)
async def async_turn_on(self):
"""Turn the switch on."""
await self.async_set_attribute(self._entity_key, True)
# Use attribute from config if available, otherwise fall back to entity_key
attribute = self._config.get("attribute", self._entity_key)
await self._async_set_status_on_off(attribute, True)
async def async_turn_off(self):
"""Turn the switch off."""
await self.async_set_attribute(self._entity_key, False)
# Use attribute from config if available, otherwise fall back to entity_key
attribute = self._config.get("attribute", self._entity_key)
await self._async_set_status_on_off(attribute, False)

View File

@@ -2,6 +2,8 @@
"config": {
"error": {
"no_home": "No available home",
"no_homes": "No available homes",
"no_homes_selected": "Please select at least one home",
"account_invalid": "Failed to authenticate on Midea cloud, the password may be changed",
"invalid_input": "Illegal input, IP address or 'auto' needed",
"login_failed": "Failed to login, wrong account or password",
@@ -38,6 +40,13 @@
"data": {
"ip_address": "IP address('auto' for discovery automatic)"
}
},
"select_homes": {
"title": "Select Homes",
"description": "Select the homes to add devices from, all selected by default",
"data": {
"selected_homes": "Select Homes"
}
}
}
},
@@ -113,6 +122,195 @@
}
},
"entity": {
"binary_sensor": {
"air_status": {
"name": "Air Running"
},
"bar_door_state": {
"name": "Bar Door State"
},
"bottom_hot": {
"name": "Bottom Hot"
},
"bright_lack": {
"name": "Brightness Lack"
},
"carpet_switch": {
"name": "Carpet Switch"
},
"chlorine_sterilization_error": {
"name": "Chlorine Sterilization Error"
},
"diy_flag": {
"name": "DIY Flag"
},
"diy_main_wash": {
"name": "DIY Main Wash"
},
"diy_piao_wash": {
"name": "DIY Rinse"
},
"diy_times": {
"name": "DIY Times"
},
"flank_hot": {
"name": "Flank Hot"
},
"flexzone_door_state": {
"name": "Flexzone Door State"
},
"freezer_door_state": {
"name": "Freezer Door State"
},
"have_reserve_task": {
"name": "Have Reserve Task"
},
"is_error": {
"name": "Is Error"
},
"maintenance_remind": {
"name": "Maintenance Remind"
},
"rtc_error": {
"name": "RTC Error"
},
"softwater_lack": {
"name": "Soft Water Lack"
},
"storage_door_state": {
"name": "Storage Door State"
},
"storage_ice_home_door_state": {
"name": "Storage Ice Home Door State"
},
"top_hot": {
"name": "Top Hot"
},
"wash_stage": {
"name": "Washing Stage"
},
"water_lack": {
"name": "Water Lack"
},
"communication_error": {
"name": "Communication Error"
},
"door_status": {
"name": "Door Status"
},
"ele_exception": {
"name": "Ele Exception"
},
"elec_warning": {
"name": "Elec Warning"
},
"limit_error": {
"name": "Limit Error"
},
"sensor_error": {
"name": "Sensor Error"
},
"lock": {
"name": "Lock"
},
"furnace_light": {
"name": "Furnace Light"
},
"dissipate_heat": {
"name": "Dissipate Heat"
},
"pre_heat": {
"name": "Pre Heat"
},
"door_open": {
"name": "Door Open"
},
"lack_water": {
"name": "Lack Water"
},
"high_temperature_work": {
"name": "High Temperature Work"
},
"lack_box": {
"name": "Lack Box"
},
"clean_sink_ponding": {
"name": "Clean Sink Ponding"
},
"clean_scale": {
"name": "Clean Scale"
},
"flip_side": {
"name": "Flip Side"
},
"reaction": {
"name": "Reaction"
},
"ramadan": {
"name": "Ramadan"
},
"change_water": {
"name": "Change Water"
},
"execute": {
"name": "Execute"
},
"power": {
"name": "Power"
},
"humidify": {
"name": "Humidify"
},
"swing": {
"name": "Swing"
},
"anion": {
"name": "Anion"
},
"display_on_off": {
"name": "Display On/Off"
},
"dust_reset": {
"name": "Dust Reset"
},
"temp_wind_switch": {
"name": "Temp Wind Switch"
},
"filter_reset": {
"name": "Filter Reset"
},
"heat_status": {
"name": "Heat Status"
},
"standby_status": {
"name": "Standby Status"
}
},
"climate": {
"freezing_zone": {
"name": "Freezing Zone"
},
"left_flexzone": {
"name": "Left Flexzone"
},
"right_flexzone": {
"name": "Right Flexzone"
},
"storage_zone": {
"name": "Storage Zone"
},
"thermostat": {
"name": "Thermostat"
},
"water_heater": {
"name": "Water Heater"
}
},
"humidifier": {
"humidifier": {
"name": "Humidifier"
}
},
"select": {
"add_rinse": {
"name": "Add Rinse"
@@ -326,6 +524,30 @@
},
"gear": {
"name": "Gear"
},
"detergent": {
"name": "Detergent"
},
"softener": {
"name": "Softener"
},
"temperature": {
"name": "Temperature"
},
"ptc": {
"name": "PTC"
},
"wind_feeling_mode": {
"name": "Wind Feeling Mode"
},
"swing_louver": {
"name": "Swing Louver"
},
"swing_horizontal": {
"name": "Swing Horizontal"
},
"swing_vertical": {
"name": "Swing Vertical"
}
},
"sensor": {
@@ -1075,189 +1297,47 @@
},
"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"
},
"room_temperature": {
"name": "Room Temperature"
},
"outside_temperature": {
"name": "Outside Temperature"
},
"lua_version": {
"name": "Lua Version"
},
"wind_speed_level": {
"name": "Wind Speed Level"
},
"timer_on_timeout": {
"name": "Timer On Timeout"
},
"timer_off_timeout": {
"name": "Timer Off Timeout"
},
"selfclean_time_left": {
"name": "Self Clean Time Left"
},
"backup_time": {
"name": "Backup Time"
},
"cur_fault_code": {
"name": "Current Fault Code"
}
},
"binary_sensor": {
"air_status": {
"name": "Air Running"
},
"bar_door_state": {
"name": "Bar Door State"
},
"bottom_hot": {
"name": "Bottom Hot"
},
"bright_lack": {
"name": "Brightness Lack"
},
"carpet_switch": {
"name": "Carpet Switch"
},
"chlorine_sterilization_error": {
"name": "Chlorine Sterilization Error"
},
"diy_flag": {
"name": "DIY Flag"
},
"diy_main_wash": {
"name": "DIY Main Wash"
},
"diy_piao_wash": {
"name": "DIY Rinse"
},
"diy_times": {
"name": "DIY Times"
},
"flank_hot": {
"name": "Flank Hot"
},
"flexzone_door_state": {
"name": "Flexzone Door State"
},
"freezer_door_state": {
"name": "Freezer Door State"
},
"have_reserve_task": {
"name": "Have Reserve Task"
},
"is_error": {
"name": "Is Error"
},
"maintenance_remind": {
"name": "Maintenance Remind"
},
"rtc_error": {
"name": "RTC Error"
},
"softwater_lack": {
"name": "Soft Water Lack"
},
"storage_door_state": {
"name": "Storage Door State"
},
"storage_ice_home_door_state": {
"name": "Storage Ice Home Door State"
},
"top_hot": {
"name": "Top Hot"
},
"wash_stage": {
"name": "Washing Stage"
},
"water_lack": {
"name": "Water Lack"
},
"communication_error": {
"name": "Communication Error"
},
"door_status": {
"name": "Door Status"
},
"ele_exception": {
"name": "Ele Exception"
},
"elec_warning": {
"name": "Elec Warning"
},
"limit_error": {
"name": "Limit Error"
},
"sensor_error": {
"name": "Sensor Error"
},
"lock": {
"name": "Lock"
},
"furnace_light": {
"name": "Furnace Light"
},
"dissipate_heat": {
"name": "Dissipate Heat"
},
"pre_heat": {
"name": "Pre Heat"
},
"door_open": {
"name": "Door Open"
},
"lack_water": {
"name": "Lack Water"
},
"high_temperature_work": {
"name": "High Temperature Work"
},
"lack_box": {
"name": "Lack Box"
},
"clean_sink_ponding": {
"name": "Clean Sink Ponding"
},
"clean_scale": {
"name": "Clean Scale"
},
"flip_side": {
"name": "Flip Side"
},
"reaction": {
"name": "Reaction"
},
"ramadan": {
"name": "Ramadan"
},
"change_water": {
"name": "Change Water"
},
"execute": {
"name": "Execute"
},
"power": {
"name": "Power"
},
"humidify": {
"name": "Humidify"
},
"swing": {
"name": "Swing"
},
"anion": {
"name": "Anion"
},
"display_on_off": {
"name": "Display On/Off"
},
"dust_reset": {
"name": "Dust Reset"
},
"temp_wind_switch": {
"name": "Temp Wind Switch"
},
"filter_reset": {
"name": "Filter Reset"
}
},
"climate": {
"freezing_zone": {
"name": "Freezing Zone"
},
"left_flexzone": {
"name": "Left Flexzone"
},
"right_flexzone": {
"name": "Right Flexzone"
},
"storage_zone": {
"name": "Storage Zone"
},
"thermostat": {
"name": "Thermostat"
},
"water_heater": {
"name": "Water Heater"
}
},
"humidifier": {
"humidifier": { "name": "Humidifier" }
},
"switch": {
"add_water_flag": {
"name": "Add Water Flag"
@@ -1628,6 +1708,9 @@
"freeze_state": {
"name": "Freeze State"
},
"fresh_air_remove_odor": {
"name": "Fresh Air Remove Odor"
},
"fresh_anion_switch": {
"name": "Fresh Anion Switch"
},
@@ -1978,6 +2061,81 @@
},
"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"
},
"eco": {
"name": "Eco Mode"
},
"strong": {
"name": "Strong Mode"
},
"sterilize": {
"name": "Sterilize"
},
"selfclean": {
"name": "Self Clean"
},
"humidification": {
"name": "Humidification"
},
"diagnose": {
"name": "Diagnose"
},
"co2_check": {
"name": "CO2 Check"
},
"hcho_check": {
"name": "HCHO Check"
},
"pm25_check": {
"name": "PM2.5 Check"
},
"idu_silent": {
"name": "IDU Silent"
},
"idu_light": {
"name": "IDU Light"
},
"idu_sleep": {
"name": "IDU Sleep"
},
"wind_feeling": {
"name": "Wind Feeling"
},
"timer_on": {
"name": "Timer On"
},
"timer_off": {
"name": "Timer Off"
},
"backup": {
"name": "Backup"
},
"cur_fault": {
"name": "Current Fault"
},
"filter_notification": {
"name": "Filter Notification"
},
"wash_mode": {
"name": "Wash Mode"
}
}
}

View File

@@ -2,6 +2,8 @@
"config": {
"error": {
"no_home": "未找到可用家庭",
"no_homes": "未找到可用家庭",
"no_homes_selected": "请至少选择一个家庭",
"account_invalid": "登录美的云服务器失败,是否已修改过密码",
"invalid_input": "无效的输入请输入有效IP地址或auto",
"login_failed": "无法登录到选择的美的云服务器,请检查用户名或密码",
@@ -38,6 +40,13 @@
"data": {
"ip_address": "设备地址(输入auto自动搜索设备)"
}
},
"select_homes": {
"title": "选择家庭",
"description": "选择要添加设备的家庭,默认全选",
"data": {
"selected_homes": "选择家庭"
}
}
}
},
@@ -269,6 +278,12 @@
},
"filter_reset": {
"name": "滤网重置"
},
"heat_status": {
"name": "加热状态"
},
"standby_status": {
"name": "待机状态"
}
},
"climate": {
@@ -509,6 +524,30 @@
},
"gear": {
"name": "档位"
},
"detergent": {
"name": "洗涤剂"
},
"softener": {
"name": "柔顺剂"
},
"temperature": {
"name": "温度"
},
"ptc": {
"name": "电辅热"
},
"wind_feeling_mode": {
"name": "风感模式"
},
"swing_louver": {
"name": "摆风叶片"
},
"swing_horizontal": {
"name": "水平摆风"
},
"swing_vertical": {
"name": "垂直摆风"
}
},
"sensor": {
@@ -1261,6 +1300,45 @@
},
"ud_diy_up_percent": {
"name": "上下自定义上百分比"
},
"water_consumption_ml": {
"name": "用水量(毫升)"
},
"cool_target_temperature": {
"name": "制冷目标温度"
},
"keep_warm_time": {
"name": "保温时间"
},
"warm_left_time": {
"name": "剩余加热时间"
},
"room_temperature": {
"name": "室内温度"
},
"outside_temperature": {
"name": "室外温度"
},
"lua_version": {
"name": "Lua版本"
},
"wind_speed_level": {
"name": "风速档位"
},
"timer_on_timeout": {
"name": "定时开机时间"
},
"timer_off_timeout": {
"name": "定时关机时间"
},
"selfclean_time_left": {
"name": "自清洁剩余时间"
},
"backup_time": {
"name": "备用时间"
},
"cur_fault_code": {
"name": "当前故障代码"
}
},
"switch": {
@@ -1633,6 +1711,9 @@
"freeze_state": {
"name": "防冻状态"
},
"fresh_air_remove_odor": {
"name": "新风除菌"
},
"fresh_anion_switch": {
"name": "负离子开关"
},
@@ -1983,6 +2064,81 @@
},
"unfreeze_power": {
"name": "解冻电源"
},
"heat_start": {
"name": "加热启动"
},
"keep_warm": {
"name": "保温"
},
"vacation": {
"name": "度假模式"
},
"germicidal": {
"name": "杀菌"
},
"drainage": {
"name": "排水"
},
"wash_enable": {
"name": "洗涤启用"
},
"eco": {
"name": "节能模式"
},
"strong": {
"name": "强劲模式"
},
"sterilize": {
"name": "杀菌功能"
},
"selfclean": {
"name": "自清洁"
},
"humidification": {
"name": "加湿功能"
},
"diagnose": {
"name": "诊断功能"
},
"co2_check": {
"name": "CO2检测"
},
"hcho_check": {
"name": "甲醛检测"
},
"pm25_check": {
"name": "PM2.5检测"
},
"idu_silent": {
"name": "室内机静音"
},
"idu_light": {
"name": "室内机灯光"
},
"idu_sleep": {
"name": "室内机睡眠"
},
"wind_feeling": {
"name": "风感功能"
},
"timer_on": {
"name": "定时开机"
},
"timer_off": {
"name": "定时关机"
},
"backup": {
"name": "备用功能"
},
"cur_fault": {
"name": "当前故障"
},
"filter_notification": {
"name": "滤网提醒"
},
"wash_mode": {
"name": "洗涤模式"
}
}
}

View File

@@ -1,14 +1,23 @@
from homeassistant.components.water_heater import WaterHeaterEntity, WaterHeaterEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
Platform,
ATTR_TEMPERATURE
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
from .midea_entity import MideaEntity
from . import load_device_config
async def async_setup_entry(hass, config_entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up water heater entities for Midea devices."""
account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
if not account_bucket:
async_add_entities([])
@@ -47,34 +56,6 @@ class MideaWaterHeaterEntityEntity(MideaEntity, WaterHeaterEntity):
rationale=rationale,
config=config,
)
self._device = device
self._manufacturer = manufacturer
self._rationale = rationale
self._config = config
# Legacy compatibility: register update and restore display attributes
if self._device:
self._device.register_update(self.update_state)
if (rationale_local := self._config.get("rationale")) is not None:
self._rationale = rationale_local
if self._rationale is None:
self._rationale = ["off", "on"]
self._attr_native_unit_of_measurement = self._config.get("unit_of_measurement")
self._attr_device_class = self._config.get("device_class")
self._attr_state_class = self._config.get("state_class")
self._attr_icon = self._config.get("icon")
from .const import DOMAIN as _DOMAIN
self._attr_unique_id = f"{_DOMAIN}.{self._device.device_id}_{self._entity_key}"
self._attr_device_info = {
"manufacturer": "Midea" if self._manufacturer is None else self._manufacturer,
"model": f"{self._device.model}",
"identifiers": {( _DOMAIN, self._device.device_id)},
"name": self._device.device_name
}
name = self._config.get("name")
if name is None:
name = self._entity_key.replace("_", " ").title()
self._attr_name = f"{self._device.device_name} {name}"
self.entity_id = self._attr_unique_id
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")
@@ -86,7 +67,7 @@ class MideaWaterHeaterEntityEntity(MideaEntity, WaterHeaterEntity):
@property
def supported_features(self):
features = 0
features = WaterHeaterEntityFeature(0)
if self._key_target_temperature is not None:
features |= WaterHeaterEntityFeature.TARGET_TEMPERATURE
if self._key_operation_list is not None:
@@ -167,9 +148,3 @@ class MideaWaterHeaterEntityEntity(MideaEntity, WaterHeaterEntity):
if new_status:
await self.async_set_attributes(new_status)
def update_state(self, status):
try:
self.schedule_update_ha_state()
except Exception:
pass