feat: add support for T0xC3 heat pump. Fix #46.

This commit is contained in:
sususweet
2025-11-27 23:03:35 +08:00
parent 8714549f90
commit 61b33e4ceb
7 changed files with 192 additions and 4 deletions

View File

@@ -33,6 +33,7 @@ Get devices from MSmartHome/Midea Meiju homes through the network and control th
- T0xB7 Gas Stove - T0xB7 Gas Stove
- T0xB8 Smart Robot Vacuum - T0xB8 Smart Robot Vacuum
- T0xBF Microwave Steam Oven - T0xBF Microwave Steam Oven
- T0xC3 Heat Pump
- T0xCA French Door Refrigerator - T0xCA French Door Refrigerator
- T0xCC Central Air Conditioning (Ducted) Wi-Fi Controller - T0xCC Central Air Conditioning (Ducted) Wi-Fi Controller
- T0xCD Air Energy Water Heater - T0xCD Air Energy Water Heater

View File

@@ -33,6 +33,7 @@
- T0xB7 燃气灶 - T0xB7 燃气灶
- T0xB8 智能扫地机器人 - T0xB8 智能扫地机器人
- T0xBF 微波炉 - T0xBF 微波炉
- T0xC3 热泵
- T0xCA 对开门冰箱 - T0xCA 对开门冰箱
- T0xCC 中央空调(风管机)Wi-Fi线控器 - T0xCC 中央空调(风管机)Wi-Fi线控器
- T0xCD 空气能热水器 - T0xCD 空气能热水器

View File

@@ -29,8 +29,8 @@ clouds = {
"app_key": "ac21b9f9cbfe4ca5a88562ef25e2b768", "app_key": "ac21b9f9cbfe4ca5a88562ef25e2b768",
"iot_key": bytes.fromhex(format(7882822598523843940, 'x')).decode(), "iot_key": bytes.fromhex(format(7882822598523843940, 'x')).decode(),
"hmac_key": bytes.fromhex(format(117390035944627627450677220413733956185864939010425, 'x')).decode(), "hmac_key": bytes.fromhex(format(117390035944627627450677220413733956185864939010425, 'x')).decode(),
# "api_url": "https://mp-eu-prod.appsmb.com/mas/v5/app/proxy?alias=", "api_url": "https://mp-eu-prod.appsmb.com/mas/v5/app/proxy?alias=",
"api_url": "https://mp-prod.appsmb.com/mas/v5/app/proxy?alias=", # "api_url": "https://mp-prod.appsmb.com/mas/v5/app/proxy?alias=",
}, },
} }
@@ -642,7 +642,7 @@ class MSmartHomeCloud(MideaCloud):
self._api_url = api_url self._api_url = api_url
async def login(self) -> bool: async def login(self) -> bool:
await self._re_route() # await self._re_route()
if login_id := await self._get_login_id(): if login_id := await self._get_login_id():
self._login_id = login_id self._login_id = login_id
iot_data = self._make_general_data() iot_data = self._make_general_data()
@@ -734,6 +734,93 @@ class MSmartHomeCloud(MideaCloud):
await fp.write(stream) await fp.write(stream)
return fnm return fnm
async def download_plugin(
self, path: str,
appliance_code: str,
smart_product_id: str,
device_type: int,
sn: str,
sn8: str,
model_number: str | None,
manufacturer_code: str = "0000",
):
# 构建 applianceList根据传入的参数动态生成
appliance_info = {
"appModel": sn8,
"appEnterprise": manufacturer_code,
"appType": f"0x{device_type:02X}",
"applianceCode": str(appliance_code) if isinstance(appliance_code, int) else appliance_code,
"smartProductId": str(smart_product_id) if isinstance(smart_product_id, int) else smart_product_id,
"modelNumber": model_number or "0",
"versionCode": 0
}
appliance_list = [appliance_info]
data = {
"applianceList": json.dumps(appliance_list),
"iotAppId": self.APP_ID,
"match": "1",
"clientType": "1",
"clientVersion": 201
}
fnm = None
if response := await self._api_request(
endpoint="/v1/plugin/update/getPluginV3",
data=data
):
# response 是 {"list": [...]}
plugin_list = response.get("list", [])
if not plugin_list:
MideaLogger.warning(f"No plugin found for device type 0x{device_type:02X}, sn: {sn}")
return None
# 找到匹配的设备(优先匹配 applianceCode其次匹配 appType
matched_plugin = None
# 首先尝试精确匹配 applianceCode
for plugin in plugin_list:
if plugin.get("applianceCode") == sn and plugin.get("appType") == f"0x{device_type:02X}":
matched_plugin = plugin
break
# 如果没有精确匹配,使用第一个匹配 appType 的
if not matched_plugin:
for plugin in plugin_list:
if plugin.get("appType") == f"0x{device_type:02X}":
matched_plugin = plugin
break
if not matched_plugin:
MideaLogger.warning(f"No matching plugin found for device type 0x{device_type:02X}, sn: {sn}")
return None
# 下载 zip 文件
zip_url = matched_plugin.get("url")
zip_title = matched_plugin.get("title", f"plugin_0x{device_type:02X}.zip")
if not zip_url:
MideaLogger.warning(f"No download URL found for plugin: {zip_title}")
return None
try:
# 确保目录存在
os.makedirs(path, exist_ok=True)
res = await self._session.get(zip_url)
if res.status == 200:
zip_data = await res.read()
if zip_data:
fnm = f"{path}/{zip_title}"
async with aiofiles.open(fnm, "wb") as fp:
await fp.write(zip_data)
MideaLogger.info(f"Downloaded plugin file: {fnm}")
else:
MideaLogger.warning(f"Downloaded zip file is empty: {zip_url}")
else:
MideaLogger.warning(f"Failed to download plugin, status: {res.status}, url: {zip_url}")
except Exception as e:
MideaLogger.error(f"Error downloading plugin: {e}")
traceback.print_exc()
return fnm
async def send_cloud(self, appliance_code: int, data: bytearray): async def send_cloud(self, appliance_code: int, data: bytearray):
appliance_code = str(appliance_code) appliance_code = str(appliance_code)
params = { params = {

View File

@@ -0,0 +1,75 @@
from homeassistant.const import Platform, UnitOfTemperature, PRECISION_HALVES, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, \
CONCENTRATION_PARTS_PER_MILLION
from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass
from homeassistant.components.switch import SwitchDeviceClass
DEVICE_MAPPING = {
"default": {
"rationale": ["off", "on"],
"queries": [{}],
"centralized": [],
"entities": {
Platform.CLIMATE: {
"Zone1": {
"power": "zone1_power_state",
"hvac_modes": {
"off": {"zone1_power_state": "off"},
"heat": {"zone1_power_state": "on"},
},
"target_temperature": "room_temp_set",
"current_temperature": "T4",
"min_temp": "room_min_set_temp",
"max_temp": "room_max_set_temp",
"temperature_unit": UnitOfTemperature.CELSIUS,
"precision": PRECISION_HALVES,
},
"DHW": {
"power": "dhw_power_state",
"hvac_modes": {
"off": {"dhw_power_state": "off"},
"heat": {"dhw_power_state": "on"},
},
"target_temperature": "dhw_temp_set",
"current_temperature": "T4",
"min_temp": "dhw_min_set_temp",
"max_temp": "dhw_max_set_temp",
"temperature_unit": UnitOfTemperature.CELSIUS,
"precision": PRECISION_HALVES,
}
},
Platform.SWITCH: {
"fastdhw_state": {
"device_class": SwitchDeviceClass.SWITCH,
"translation_key": "fastdhw_state",
},
"forcetbh_state": {
"device_class": SwitchDeviceClass.SWITCH,
"translation_key": "forcetbh_state",
},
},
Platform.SENSOR: {
"run_mode_set": {
"device_class": SensorDeviceClass.ENUM,
"translation_key": "mode",
},
"room_temp_set": {
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT,
"translation_key": "room_temperature",
},
"t4": {
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT,
"translation_key": "outside_temperature",
},
"tank_actual_temp":{
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT,
}
}
}
}
}

View File

@@ -125,7 +125,7 @@ class MideaEntity(CoordinatorEntity[MideaDataUpdateCoordinator], Entity):
@property @property
def device_attributes(self) -> dict: def device_attributes(self) -> dict:
"""Return device attributes.""" """Return device attributes."""
return self.coordinator.data.attributes return self.coordinator.data.attributes if self.coordinator.data else {}
@property @property
def available(self) -> bool: def available(self) -> bool:

View File

@@ -335,6 +335,12 @@
} }
} }
}, },
"Zone1": {
"name": "区域1"
},
"DHW": {
"name": "DHW"
},
"colmo_turing_central_ac_climate": { "colmo_turing_central_ac_climate": {
"name": "Thermostat", "name": "Thermostat",
"state_attributes": { "state_attributes": {
@@ -2567,6 +2573,12 @@
}, },
"water_model_go_out": { "water_model_go_out": {
"name": "Away Mode" "name": "Away Mode"
},
"fastdhw_state": {
"name": "Fast Hot Water"
},
"forcetbh_state": {
"name": "Force Standby"
} }
} }
} }

View File

@@ -335,6 +335,12 @@
} }
} }
}, },
"Zone1": {
"name": "区域1"
},
"DHW": {
"name": "DHW"
},
"colmo_turing_central_ac_climate": { "colmo_turing_central_ac_climate": {
"name": "温控器", "name": "温控器",
"state_attributes": { "state_attributes": {
@@ -2571,6 +2577,12 @@
}, },
"water_model_go_out": { "water_model_go_out": {
"name": "外出模式" "name": "外出模式"
},
"fastdhw_state": {
"name": "快速生活热水"
},
"forcetbh_state": {
"name": "强制待机"
} }
} }
} }