14 Commits

Author SHA1 Message Date
sususweet
15ac35a887 fix: async import module. 2025-11-21 09:49:35 +08:00
sususweet
2858e991ae feat: add support for T0xBF. Fix #36. 2025-11-21 09:39:42 +08:00
Yingqi Tang
fe1a5b315b fix: file async read 2025-11-21 01:16:55 +00:00
Yingqi Tang
849280676f feat: add follow_body_sense for T0xAC. Fixes #51 2025-11-21 01:10:45 +00:00
Yingqi Tang
3edd94d790 fix: refresh status fallback to lua when cloud api is down. Fixes #52. 2025-11-21 01:05:55 +00:00
sususweet
730f7d0c7b feat: update version 2025-11-19 02:11:39 +08:00
sususweet
b2a2c80133 feat: update wash function for T0xED. 2025-11-19 02:06:47 +08:00
sususweet
cbaf99592f fix: get_status attributes missing. 2025-11-19 01:59:39 +08:00
sususweet
6062b0534f feat: add support for MSmartHome App. 2025-11-19 01:48:11 +08:00
sususweet
5369e6c13d feat: version 0.1.23 2025-11-14 21:15:14 +08:00
sususweet
2a91ce9914 fix: add entities for T0xB6. Ref #45. 2025-11-14 21:14:11 +08:00
sususweet
52c10f7d0d fix: multi-point login and control for meiju app. 2025-11-14 20:52:03 +08:00
sususweet
c090edfb3d fix: bubble attribute for T0xE3. 2025-11-14 20:51:46 +08:00
sususweet
e3a1812f9a feat: fix cold water single loop support for T0xE3. 2025-11-13 10:09:40 +08:00
15 changed files with 279 additions and 128 deletions

View File

@@ -32,6 +32,7 @@ Get devices from MSmartHome/Midea Meiju homes through the network and control th
- T0xB6 Range Hood - T0xB6 Range Hood
- T0xB7 Gas Stove - T0xB7 Gas Stove
- T0xB8 Smart Robot Vacuum - T0xB8 Smart Robot Vacuum
- T0xBF Microwave Steam Oven
- 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

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

View File

@@ -1,3 +1,4 @@
import asyncio
import os import os
import base64 import base64
from importlib import import_module from importlib import import_module
@@ -62,6 +63,9 @@ PLATFORMS: list[Platform] = [
Platform.BUTTON Platform.BUTTON
] ]
async def import_module_async(module_name):
# 在线程池中执行导入操作
return await asyncio.to_thread(import_module, module_name, __package__)
def get_sn8_used(hass: HomeAssistant, sn8): def get_sn8_used(hass: HomeAssistant, sn8):
entries = hass.config_entries.async_entries(DOMAIN) entries = hass.config_entries.async_entries(DOMAIN)
@@ -102,7 +106,7 @@ async def load_device_config(hass: HomeAssistant, device_type, sn8):
# if not json_data: # if not json_data:
device_path = f".device_mapping.{'T0x%02X' % device_type}" device_path = f".device_mapping.{'T0x%02X' % device_type}"
try: try:
mapping_module = import_module(device_path, __package__) mapping_module = await import_module_async(device_path)
for key, config in mapping_module.DEVICE_MAPPING.items(): for key, config in mapping_module.DEVICE_MAPPING.items():
# support tuple & regular expression pattern to support multiple sn8 sharing one mapping # support tuple & regular expression pattern to support multiple sn8 sharing one mapping
if (key == sn8) or (isinstance(key, tuple) and sn8 in key) or (isinstance(key, str) and re.match(key, sn8)): if (key == sn8) or (isinstance(key, tuple) and sn8 in key) or (isinstance(key, str) and re.match(key, sn8)):

View File

@@ -85,6 +85,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
self._key_target_humidity = self._config.get("target_humidity") self._key_target_humidity = self._config.get("target_humidity")
self._attr_temperature_unit = self._config.get("temperature_unit") self._attr_temperature_unit = self._config.get("temperature_unit")
self._attr_precision = self._config.get("precision") self._attr_precision = self._config.get("precision")
self._attr_target_temperature_step = self._config.get("precision")
@property @property
def supported_features(self): def supported_features(self):

View File

@@ -4,6 +4,7 @@ import datetime
import json import json
import base64 import base64
import asyncio import asyncio
import aiofiles
import requests import requests
from aiohttp import ClientSession from aiohttp import ClientSession
from secrets import token_hex from secrets import token_hex
@@ -89,12 +90,10 @@ class MideaCloud:
"accesstoken": self._access_token "accesstoken": self._access_token
}) })
response:dict = {"code": -1} response:dict = {"code": -1}
_LOGGER.debug(f"Midea cloud API header: {header}")
_LOGGER.debug(f"Midea cloud API dump_data: {dump_data}")
try: try:
r = await self._session.request(method, url, headers=header, data=dump_data, timeout=5) r = await self._session.request(method, url, headers=header, data=dump_data, timeout=5)
raw = await r.read() raw = await r.read()
_LOGGER.debug(f"Midea cloud API url: {url}, data: {data}, response: {raw}") _LOGGER.debug(f"Midea cloud API url: {url}, header: {header}, data: {data}, response: {raw}")
response = json.loads(raw) response = json.loads(raw)
except Exception as e: except Exception as e:
_LOGGER.debug(f"API request attempt failed: {e}") _LOGGER.debug(f"API request attempt failed: {e}")
@@ -162,28 +161,6 @@ class MideaCloud:
async def login(self) -> bool: async def login(self) -> bool:
raise NotImplementedError() raise NotImplementedError()
async def send_cloud(self, appliance_id: int, data: bytearray):
appliance_code = str(appliance_id)
params = {
'applianceCode': appliance_code,
'order': self._security.aes_encrypt(bytes_to_dec_string(data)).hex(),
'timestamp': 'true',
"isFull": "false"
}
if response := await self._api_request(
endpoint='/v1/appliance/transparent/send',
data=params,
):
if response and response.get('reply'):
_LOGGER.debug("[%s] Cloud command response: %s", appliance_code, response)
reply_data = self._security.aes_decrypt(bytes.fromhex(response['reply']))
return reply_data
else:
_LOGGER.warning("[%s] Cloud command failed: %s", appliance_code, response)
return None
async def list_home(self) -> dict | None: async def list_home(self) -> dict | None:
return {1: "My home"} return {1: "My home"}
@@ -199,10 +176,6 @@ class MideaCloud:
): ):
raise NotImplementedError() raise NotImplementedError()
async def send_device_control(self, appliance_code: int, control: dict, status: dict | None = None) -> bool:
"""Send control to a device via cloud. Subclasses should implement if supported."""
raise NotImplementedError()
async def send_central_ac_control(self, appliance_code: int, nodeid: str, modelid: str, idtype: int, control: dict) -> bool: async def send_central_ac_control(self, appliance_code: int, nodeid: str, modelid: str, idtype: int, control: dict) -> bool:
"""Send control to central AC subdevice. Subclasses should implement if supported.""" """Send control to central AC subdevice. Subclasses should implement if supported."""
raise NotImplementedError() raise NotImplementedError()
@@ -323,6 +296,28 @@ class MeijuCloud(MideaCloud):
return appliances return appliances
return None return None
async def send_cloud(self, appliance_id: int, data: bytearray):
appliance_code = str(appliance_id)
params = {
'applianceCode': appliance_code,
'order': self._security.aes_encrypt(bytes_to_dec_string(data)).hex(),
'timestamp': 'true',
"isFull": "false"
}
if response := await self._api_request(
endpoint='/v1/appliance/transparent/send',
data=params,
):
if response and response.get('reply'):
_LOGGER.debug("[%s] Cloud command response: %s", appliance_code, response)
reply_data = self._security.aes_decrypt(bytes.fromhex(response['reply']))
return reply_data
else:
_LOGGER.warning("[%s] Cloud command failed: %s", appliance_code, response)
return None
async def get_device_status(self, appliance_code: int, query: dict) -> dict | None: async def get_device_status(self, appliance_code: int, query: dict) -> dict | None:
data = { data = {
"applianceCode": str(appliance_code), "applianceCode": str(appliance_code),
@@ -471,8 +466,8 @@ class MeijuCloud(MideaCloud):
self._security.aes_decrypt_with_fixed_key(lua)) self._security.aes_decrypt_with_fixed_key(lua))
stream = stream.replace("\r\n", "\n") stream = stream.replace("\r\n", "\n")
fnm = f"{path}/{response['fileName']}" fnm = f"{path}/{response['fileName']}"
with open(fnm, "w") as fp: async with aiofiles.open(fnm, "w") as fp:
fp.write(stream) await fp.write(stream)
return fnm return fnm
@@ -633,10 +628,36 @@ class MSmartHomeCloud(MideaCloud):
self._security.aes_decrypt_with_fixed_key(lua)) self._security.aes_decrypt_with_fixed_key(lua))
stream = stream.replace("\r\n", "\n") stream = stream.replace("\r\n", "\n")
fnm = f"{path}/{response['fileName']}" fnm = f"{path}/{response['fileName']}"
with open(fnm, "w") as fp: async with aiofiles.open(fnm, "w") as fp:
fp.write(stream) await fp.write(stream)
return fnm return fnm
async def send_cloud(self, appliance_code: int, data: bytearray):
appliance_code = str(appliance_code)
params = {
"clientType": "1",
"appId": self.APP_ID,
"format": "2",
"deviceId": self._device_id,
"applianceCode": appliance_code,
'order': self._security.aes_encrypt(bytes_to_dec_string(data)).hex(),
'timestamp': 'true',
"isFull": "false"
}
if response := await self._api_request(
endpoint='/v1/appliance/transparent/send',
data=params,
):
if response and response.get('reply'):
_LOGGER.debug("[%s] Cloud command response: %s", appliance_code, response)
reply_data = self._security.aes_decrypt(bytes.fromhex(response['reply']))
return reply_data
else:
_LOGGER.warning("[%s] Cloud command failed: %s", appliance_code, response)
return None
async def get_device_status( async def get_device_status(
self, self,
appliance_code: int, appliance_code: int,
@@ -671,9 +692,29 @@ class MSmartHomeCloud(MideaCloud):
return response return response
return None return None
async def send_device_control(self, appliance_code: int, control: dict, status: dict | None = None) -> bool: async def send_device_control(
self,
appliance_code: int,
device_type: int,
sn: str,
model_number: str | None,
manufacturer_code: str = "0000",
control: dict | None = None,
status: dict | None = None
) -> bool:
data = { data = {
"applianceCode": str(appliance_code), "clientType": "1",
"appId": self.APP_ID,
"format": "2",
"deviceId": self._device_id,
"iotAppId": self.APP_ID,
"applianceMFCode": manufacturer_code,
"applianceType": "0x%02X" % device_type,
"modelNumber": model_number,
"applianceSn": self._security.aes_encrypt_with_fixed_key(sn.encode("ascii")).hex(),
"version": "0",
"encryptedType ": "2",
"applianceCode": appliance_code,
"command": { "command": {
"control": control "control": control
} }

View File

@@ -3,7 +3,7 @@ import socket
import traceback import traceback
from enum import IntEnum from enum import IntEnum
from .cloud import MideaCloud, MSmartHomeCloud from .cloud import MideaCloud, MSmartHomeCloud, MeijuCloud
from .security import LocalSecurity, MSGTYPE_HANDSHAKE_REQUEST, MSGTYPE_ENCRYPTED_REQUEST from .security import LocalSecurity, MSGTYPE_HANDSHAKE_REQUEST, MSGTYPE_ENCRYPTED_REQUEST
from .packet_builder import PacketBuilder from .packet_builder import PacketBuilder
from .message import MessageQuestCustom from .message import MessageQuestCustom
@@ -180,7 +180,17 @@ class MiedaDevice(threading.Thread):
cloud = self._cloud cloud = self._cloud
if cloud and hasattr(cloud, "send_device_control"): if cloud and hasattr(cloud, "send_device_control"):
await cloud.send_device_control(self._device_id, control=nested_status, status=self._attributes) if isinstance(cloud, MSmartHomeCloud):
await cloud.send_device_control(
appliance_code=self._device_id,
device_type=self.device_type,
sn=self.sn,
model_number=self.subtype,
manufacturer_code=self._manufacturer_code,
control=nested_status,
status=self._attributes)
elif isinstance(cloud, MeijuCloud):
await cloud.send_device_control(self._device_id, control=nested_status, status=self._attributes)
async def set_attributes(self, attributes): async def set_attributes(self, attributes):
new_status = {} new_status = {}
@@ -207,7 +217,17 @@ class MiedaDevice(threading.Thread):
cloud = self._cloud cloud = self._cloud
if cloud and hasattr(cloud, "send_device_control"): if cloud and hasattr(cloud, "send_device_control"):
await cloud.send_device_control(self._device_id, control=nested_status, status=self._attributes) if isinstance(cloud, MSmartHomeCloud):
await cloud.send_device_control(
appliance_code=self._device_id,
device_type=self.device_type,
sn=self.sn,
model_number=self.subtype,
manufacturer_code=self._manufacturer_code,
control=nested_status,
status=self._attributes)
elif isinstance(cloud, MeijuCloud):
await cloud.send_device_control(self._device_id, control=nested_status, status=self._attributes)
def set_ip_address(self, ip_address): def set_ip_address(self, ip_address):
MideaLogger.debug(f"Update IP address to {ip_address}") MideaLogger.debug(f"Update IP address to {ip_address}")
@@ -274,28 +294,30 @@ class MiedaDevice(threading.Thread):
cloud = self._cloud cloud = self._cloud
if cloud and hasattr(cloud, "get_device_status"): if cloud and hasattr(cloud, "get_device_status"):
if isinstance(cloud, MSmartHomeCloud): if isinstance(cloud, MSmartHomeCloud):
status = await cloud.get_device_status( if status := await cloud.get_device_status(
appliance_code=self._device_id, appliance_code=self._device_id,
device_type=self.device_type, device_type=self.device_type,
sn=self.sn, sn=self.sn,
model_number=self.subtype, model_number=self.subtype,
manufacturer_code=self._manufacturer_code, manufacturer_code=self._manufacturer_code,
query=query query=query
) ):
else: self._parse_cloud_message(status)
status = await cloud.get_device_status( else:
if self._lua_runtime is not None:
if query_cmd := self._lua_runtime.build_query(query):
await self._build_send(query_cmd)
elif isinstance(cloud, MeijuCloud):
if status := await cloud.get_device_status(
appliance_code=self._device_id, appliance_code=self._device_id,
query=query query=query
) ):
self._parse_cloud_message(status)
self._parse_cloud_message(status) else:
# if self._lua_runtime is not None: if self._lua_runtime is not None:
# if query_cmd := self._lua_runtime.build_query(query): if query_cmd := self._lua_runtime.build_query(query):
# try: await self._build_send(query_cmd)
# await self._build_send(query_cmd)
# return
# except Exception as e:
# traceback.print_exc()
def _parse_cloud_message(self, status): def _parse_cloud_message(self, status):

View File

@@ -107,7 +107,10 @@ DEVICE_MAPPING = {
}, },
"aux_heat": { "aux_heat": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
} },
"follow_body_sense": {
"device_class": SwitchDeviceClass.SWITCH,
},
}, },
Platform.SENSOR: { Platform.SENSOR: {
"mode": { "mode": {
@@ -252,16 +255,13 @@ DEVICE_MAPPING = {
}, },
Platform.SWITCH: { Platform.SWITCH: {
"dry": { "dry": {
"name": "干燥",
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
}, },
"prevent_straight_wind": { "prevent_straight_wind": {
"name": "防直吹",
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
"rationale": [1, 2] "rationale": [1, 2]
}, },
"aux_heat": { "aux_heat": {
"name": "电辅热",
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
} }
}, },
@@ -357,7 +357,6 @@ DEVICE_MAPPING = {
}, },
Platform.SWITCH: { Platform.SWITCH: {
"power": { "power": {
"name": "电源",
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
}, },
}, },

View File

@@ -1,4 +1,5 @@
from homeassistant.const import Platform from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass
from homeassistant.const import Platform, UnitOfPower, UnitOfElectricPotential
from homeassistant.components.switch import SwitchDeviceClass from homeassistant.components.switch import SwitchDeviceClass
DEVICE_MAPPING = { DEVICE_MAPPING = {
@@ -6,6 +7,14 @@ DEVICE_MAPPING = {
"rationale": ["off", "on"], "rationale": ["off", "on"],
"queries": [{}], "queries": [{}],
"centralized": [], "centralized": [],
"calculate": {
"get": [
{
"lvalue": "[b7_vbattery]",
"rvalue": "[b7_vbatt] / 1000.0"
},
],
},
"entities": { "entities": {
Platform.SWITCH: { Platform.SWITCH: {
"power": { "power": {
@@ -18,6 +27,25 @@ DEVICE_MAPPING = {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
} }
}, },
Platform.SENSOR: {
"error_code": {
"device_class": SensorDeviceClass.ENUM
},
"b7_left_status": {
"device_class": SensorDeviceClass.ENUM,
"translation_key": "left_status",
},
"b7_right_status": {
"device_class": SensorDeviceClass.ENUM,
"translation_key": "right_status",
},
"b7_vbattery":{
"device_class": SensorDeviceClass.VOLTAGE,
"unit_of_measurement": UnitOfElectricPotential.VOLT,
"state_class": SensorStateClass.MEASUREMENT,
"translation_key": "battery_voltage",
}
},
Platform.SELECT: { Platform.SELECT: {
"wind_pressure": { "wind_pressure": {
"options": { "options": {
@@ -28,6 +56,15 @@ DEVICE_MAPPING = {
"extreme": {"wind_pressure": "4"}, "extreme": {"wind_pressure": "4"},
} }
}, },
"gear": {
"options": {
"off": {"gear": 0},
"low": {"gear": 1},
"medium": {"gear": 2},
"high": {"gear": 3},
"extreme": {"gear": 4},
}
},
}, },
} }
} }

View File

@@ -0,0 +1,65 @@
from homeassistant.const import Platform, UnitOfTime, UnitOfArea, UnitOfTemperature
from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
DEVICE_MAPPING = {
"default": {
"rationale": ["off", "on"],
"queries": [{}],
"centralized": [],
"calculate": {
"get": [
{
"lvalue": "[work_time]",
"rvalue": "[work_second] + 60 * [work_minute] + 3600 * [work_hour]"
},
{
"lvalue": "[set_time]",
"rvalue": "[second_set] + 60 * [minute_set] + 3600 * [hour_set]"
}
],
"set": [
]
},
"entities": {
Platform.BINARY_SENSOR: {
"lack_water": {
"device_class": BinarySensorDeviceClass.RUNNING,
"rationale": [0, 1]
},
"door_open": {
"device_class": BinarySensorDeviceClass.RUNNING,
},
"change_water": {
"device_class": BinarySensorDeviceClass.RUNNING,
"rationale": [0, 1]
}
},
Platform.SENSOR: {
"work_status": {
"device_class": SensorDeviceClass.ENUM
},
"cur_temperature_above": {
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT
},
"cur_temperature_underside": {
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT
},
"work_time": {
"device_class": SensorDeviceClass.DURATION,
"unit_of_measurement": UnitOfTime.SECONDS,
"state_class": SensorStateClass.MEASUREMENT
},
"set_time": {
"device_class": SensorDeviceClass.DURATION,
"unit_of_measurement": UnitOfTime.SECONDS,
"state_class": SensorStateClass.MEASUREMENT
},
}
}
}
}

View File

@@ -1,5 +1,4 @@
from homeassistant.const import Platform, UnitOfTemperature, UnitOfTime, PERCENTAGE, PRECISION_HALVES, \ from homeassistant.const import Platform, UnitOfTemperature, UnitOfTime, PRECISION_WHOLE
CONCENTRATION_PARTS_PER_MILLION
from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass
from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.switch import SwitchDeviceClass from homeassistant.components.switch import SwitchDeviceClass
@@ -16,10 +15,6 @@ DEVICE_MAPPING = {
"min": 0, "min": 0,
"max": 3, "max": 3,
"step": 1 "step": 1
},
"cur_temperature": {
"device_class": SensorDeviceClass.TEMPERATURE,
"state_class": SensorStateClass.MEASUREMENT,
} }
}, },
Platform.CLIMATE: { Platform.CLIMATE: {
@@ -32,9 +27,9 @@ DEVICE_MAPPING = {
"target_temperature": "temperature", "target_temperature": "temperature",
"current_temperature": "cur_temperature", "current_temperature": "cur_temperature",
"min_temp": 30, "min_temp": 30,
"max_temp": 75, "max_temp": 80,
"temperature_unit": UnitOfTemperature.CELSIUS, "temperature_unit": UnitOfTemperature.CELSIUS,
"precision": PRECISION_HALVES, "precision": PRECISION_WHOLE,
} }
}, },
Platform.SWITCH: { Platform.SWITCH: {
@@ -62,25 +57,8 @@ DEVICE_MAPPING = {
"water_flow": { "water_flow": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
}, },
}, "sterilization": {
Platform.SELECT: { "device_class": SwitchDeviceClass.SWITCH,
"func_select": {
"options": {
"low": {"func_select": "low"},
"medium": {"func_select": "medium"}
}
},
"type_select": {
"options": {
"normal": {"type_select": "normal"},
"valve": {"type_select": "valve"},
}
},
"machine": {
"options": {
"real_machine": {"machine": "real_machine"},
"virtual_machine": {"machine": "virtual_machine"}
}
} }
}, },
Platform.SENSOR: { Platform.SENSOR: {
@@ -112,22 +90,18 @@ DEVICE_MAPPING = {
"passwater_lowbyte": { "passwater_lowbyte": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.WATER,
"unit_of_measurement": "L", "unit_of_measurement": "L",
"state_class": SensorStateClass.MEASUREMENT "state_class": SensorStateClass.TOTAL
}, },
"passwater_highbyte": { "passwater_highbyte": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.WATER,
"unit_of_measurement": "L", "unit_of_measurement": "L",
"state_class": SensorStateClass.MEASUREMENT "state_class": SensorStateClass.TOTAL
}, },
"rate": { "rate": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.ENUM
"unit_of_measurement": "L/min",
"state_class": SensorStateClass.MEASUREMENT
}, },
"cur_rate": { "cur_rate": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.ENUM
"unit_of_measurement": "L/min",
"state_class": SensorStateClass.MEASUREMENT
}, },
"sterilize_left_days": { "sterilize_left_days": {
"device_class": SensorDeviceClass.DURATION, "device_class": SensorDeviceClass.DURATION,
@@ -155,19 +129,13 @@ DEVICE_MAPPING = {
"state_class": SensorStateClass.MEASUREMENT "state_class": SensorStateClass.MEASUREMENT
}, },
"tds_value": { "tds_value": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.ENUM
"unit_of_measurement": CONCENTRATION_PARTS_PER_MILLION,
"state_class": SensorStateClass.MEASUREMENT
}, },
"heat_water_level": { "heat_water_level": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.ENUM
"unit_of_measurement": "%",
"state_class": SensorStateClass.MEASUREMENT
}, },
"flow": { "flow": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.ENUM
"unit_of_measurement": "L/min",
"state_class": SensorStateClass.MEASUREMENT
}, },
"end_time_hour": { "end_time_hour": {
"device_class": SensorDeviceClass.DURATION, "device_class": SensorDeviceClass.DURATION,
@@ -202,19 +170,17 @@ DEVICE_MAPPING = {
"state_class": SensorStateClass.MEASUREMENT "state_class": SensorStateClass.MEASUREMENT
}, },
"mg_remain": { "mg_remain": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.ENUM
"unit_of_measurement": "mg",
"state_class": SensorStateClass.MEASUREMENT
}, },
"waterday_lowbyte": { "waterday_lowbyte": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.WATER,
"unit_of_measurement": "L", "unit_of_measurement": "L",
"state_class": SensorStateClass.MEASUREMENT "state_class": SensorStateClass.TOTAL
}, },
"waterday_highbyte": { "waterday_highbyte": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.WATER,
"unit_of_measurement": "L", "unit_of_measurement": "L",
"state_class": SensorStateClass.MEASUREMENT "state_class": SensorStateClass.TOTAL
} }
}, },
Platform.BINARY_SENSOR: { Platform.BINARY_SENSOR: {

View File

@@ -15,7 +15,9 @@ DEVICE_MAPPING = {
}, },
"bubble": { "bubble": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
"rationale": [0, 1] },
"cold_water": {
"device_class": SwitchDeviceClass.SWITCH,
}, },
"cold_water_master": { "cold_water_master": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,

View File

@@ -17,9 +17,6 @@ DEVICE_MAPPING = {
"heat": { "heat": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
}, },
"antifreeze": {
"device_class": SwitchDeviceClass.SWITCH,
},
"lock": { "lock": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
}, },
@@ -29,22 +26,13 @@ DEVICE_MAPPING = {
"keep_warm": { "keep_warm": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
}, },
"vacation": {
"device_class": SwitchDeviceClass.SWITCH,
},
"germicidal": { "germicidal": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
}, },
"lack_water": {
"device_class": SwitchDeviceClass.SWITCH,
},
"drainage": { "drainage": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
}, },
"wash_enable": { "wash": {
"device_class": SwitchDeviceClass.SWITCH,
},
"water_way": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
}, },
"soften": { "soften": {
@@ -65,14 +53,14 @@ DEVICE_MAPPING = {
"cl_sterilization": { "cl_sterilization": {
"device_class": SwitchDeviceClass.SWITCH, "device_class": SwitchDeviceClass.SWITCH,
}, },
"holiday_mode": {
"device_class": SwitchDeviceClass.SWITCH,
},
}, },
Platform.BINARY_SENSOR: { Platform.BINARY_SENSOR: {
"heat_status": { "heat_status": {
"device_class": BinarySensorDeviceClass.RUNNING, "device_class": BinarySensorDeviceClass.RUNNING,
}, },
"lack_water": {
"device_class": BinarySensorDeviceClass.PROBLEM,
},
"standby_status": { "standby_status": {
"device_class": BinarySensorDeviceClass.RUNNING, "device_class": BinarySensorDeviceClass.RUNNING,
}, },

View File

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

View File

@@ -651,6 +651,9 @@
"move_direction": { "move_direction": {
"name": "Move Direction" "name": "Move Direction"
}, },
"work_status": {
"name": "Work Status"
},
"sub_work_status": { "sub_work_status": {
"name": "Sub Work Status" "name": "Sub Work Status"
}, },
@@ -1206,6 +1209,9 @@
"work_time": { "work_time": {
"name": "Work Time" "name": "Work Time"
}, },
"set_time": {
"name": "Set Time"
},
"zero_cold_tem": { "zero_cold_tem": {
"name": "Zero Cold Temperature" "name": "Zero Cold Temperature"
}, },
@@ -1443,6 +1449,9 @@
"left_status": { "left_status": {
"name": "Left Status" "name": "Left Status"
}, },
"right_status": {
"name": "Right Status"
},
"eq_recipe_action": { "eq_recipe_action": {
"name": "Eq Recipe Action" "name": "Eq Recipe Action"
}, },
@@ -1571,6 +1580,9 @@
} }
}, },
"switch": { "switch": {
"follow_body_sense": {
"name": "Follow Body Sense"
},
"waterions": { "waterions": {
"name": "Disinfection" "name": "Disinfection"
}, },
@@ -1827,7 +1839,7 @@
"name": "CO2 Check Enable" "name": "CO2 Check Enable"
}, },
"cold_water": { "cold_water": {
"name": "Cold Water" "name": "Cold Water Single Loop"
}, },
"cold_water_ai": { "cold_water_ai": {
"name": "Cold Water AI" "name": "Cold Water AI"

View File

@@ -655,6 +655,9 @@
"move_direction": { "move_direction": {
"name": "移动方向" "name": "移动方向"
}, },
"work_status": {
"name": "工作状态"
},
"sub_work_status": { "sub_work_status": {
"name": "子工作状态" "name": "子工作状态"
}, },
@@ -1210,6 +1213,9 @@
"work_time": { "work_time": {
"name": "工作时间" "name": "工作时间"
}, },
"set_time": {
"name": "设置工作时间"
},
"zero_cold_tem": { "zero_cold_tem": {
"name": "零冷水温度" "name": "零冷水温度"
}, },
@@ -1447,6 +1453,9 @@
"left_status": { "left_status": {
"name": "左侧状态" "name": "左侧状态"
}, },
"right_status": {
"name": "右侧状态"
},
"eq_recipe_action": { "eq_recipe_action": {
"name": "等量食谱动作" "name": "等量食谱动作"
}, },
@@ -1575,6 +1584,9 @@
} }
}, },
"switch": { "switch": {
"follow_body_sense": {
"name": "随身感"
},
"waterions": { "waterions": {
"name": "消杀" "name": "消杀"
}, },
@@ -1831,7 +1843,7 @@
"name": "CO2检测启用" "name": "CO2检测启用"
}, },
"cold_water": { "cold_water": {
"name": "零冷水" "name": "零冷水单次循环"
}, },
"cold_water_ai": { "cold_water_ai": {
"name": "AI零冷水" "name": "AI零冷水"