9 Commits

Author SHA1 Message Date
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
sususweet
cc5ec70819 feat: add support for T0xFC. Fix #47. 2025-11-11 15:34:30 +08:00
sususweet
37ace3d764 feat: add cloud api for SmartHome app 2025-11-11 14:24:10 +08:00
sususweet
7476d6ad1d feat: add support for T0xCD 2025-11-07 01:08:38 +08:00
sususweet
883369184b feat: fix support for T0xE2 2025-11-06 22:31:08 +08:00
17 changed files with 441 additions and 82 deletions

View File

@@ -34,6 +34,7 @@ Get devices from MSmartHome/Midea Meiju homes through the network and control th
- T0xB8 Smart Robot Vacuum - T0xB8 Smart Robot Vacuum
- 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
- T0xCE Fresh Air System - T0xCE Fresh Air System
- T0xCF Central Air Conditioning Heating - T0xCF Central Air Conditioning Heating
- T0xD9 Twin Tub Washing Machine - T0xD9 Twin Tub Washing Machine
@@ -47,6 +48,7 @@ Get devices from MSmartHome/Midea Meiju homes through the network and control th
- T0xED Water Softener - T0xED Water Softener
- T0xFA Electric Fan - T0xFA Electric Fan
- T0xFB Electric Heater - T0xFB Electric Heater
- T0xFC Air Purifier
- T0xFD Humidifier - T0xFD Humidifier
Welcome to collaborate on adding support for more devices. Welcome to collaborate on adding support for more devices.

View File

@@ -34,6 +34,7 @@
- T0xB8 智能扫地机器人 - T0xB8 智能扫地机器人
- T0xCA 对开门冰箱 - T0xCA 对开门冰箱
- T0xCC 中央空调(风管机)Wi-Fi线控器 - T0xCC 中央空调(风管机)Wi-Fi线控器
- T0xCD 空气能热水器
- T0xCE 新风机 - T0xCE 新风机
- T0xCF 中央空调暖家 - T0xCF 中央空调暖家
- T0xD9 复式洗衣机 - T0xD9 复式洗衣机
@@ -47,6 +48,7 @@
- T0xED 软水机 - T0xED 软水机
- T0xFA 电风扇 - T0xFA 电风扇
- T0xFB 电暖器 - T0xFB 电暖器
- T0xFC 空气净化器
- T0xFD 加湿器 - T0xFD 加湿器
欢迎合作开发添加更多设备支持。 欢迎合作开发添加更多设备支持。

View File

@@ -258,6 +258,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
protocol=info.get(CONF_PROTOCOL) or 2, protocol=info.get(CONF_PROTOCOL) or 2,
model=info.get(CONF_MODEL), model=info.get(CONF_MODEL),
subtype=info.get(CONF_MODEL_NUMBER), subtype=info.get(CONF_MODEL_NUMBER),
manufacturer_code=info.get(CONF_MANUFACTURER_CODE),
sn=info.get(CONF_SN), sn=info.get(CONF_SN),
sn8=info.get(CONF_SN8), sn8=info.get(CONF_SN8),
lua_file=file, lua_file=file,

View File

@@ -27,6 +27,7 @@ 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-prod.appsmb.com/mas/v5/app/proxy?alias=", "api_url": "https://mp-prod.appsmb.com/mas/v5/app/proxy?alias=",
}, },
} }
@@ -636,6 +637,55 @@ class MSmartHomeCloud(MideaCloud):
fp.write(stream) fp.write(stream)
return fnm return fnm
async def get_device_status(
self,
appliance_code: int,
device_type: int,
sn: str,
model_number: str | None,
manufacturer_code: str = "0000",
query: dict = {}
) -> dict | None:
data = {
"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": {
"query": query
}
}
if response := await self._api_request(
endpoint="/v1/device/status/lua/get",
data=data
):
# 预期返回形如 { ... 状态键 ... }
return response
return None
async def send_device_control(self, appliance_code: int, control: dict, status: dict | None = None) -> bool:
data = {
"applianceCode": str(appliance_code),
"command": {
"control": control
}
}
if status and isinstance(status, dict):
data["command"]["status"] = status
response = await self._api_request(
endpoint="/v1/device/lua/control",
data=data
)
return response is not None
def get_midea_cloud(cloud_name: str, session: ClientSession, account: str, password: str) -> MideaCloud | None: def get_midea_cloud(cloud_name: str, session: ClientSession, account: str, password: str) -> MideaCloud | None:
cloud = None cloud = None

View File

@@ -3,7 +3,7 @@ import socket
import traceback import traceback
from enum import IntEnum from enum import IntEnum
from .cloud import MideaCloud from .cloud import MideaCloud, MSmartHomeCloud
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
@@ -42,6 +42,7 @@ class MiedaDevice(threading.Thread):
protocol: int, protocol: int,
model: str | None, model: str | None,
subtype: int | None, subtype: int | None,
manufacturer_code: str | None,
connected: bool, connected: bool,
sn: str | None, sn: str | None,
sn8: str | None, sn8: str | None,
@@ -65,6 +66,7 @@ class MiedaDevice(threading.Thread):
self._subtype = subtype self._subtype = subtype
self._sn = sn self._sn = sn
self._sn8 = sn8 self._sn8 = sn8
self._manufacturer_code = manufacturer_code
self._attributes = { self._attributes = {
"device_type": "T0x%02X" % device_type, "device_type": "T0x%02X" % device_type,
"sn": sn, "sn": sn,
@@ -269,47 +271,70 @@ class MiedaDevice(threading.Thread):
async def refresh_status(self): async def refresh_status(self):
for query in self._queries: for query in self._queries:
if self._lua_runtime is not None: cloud = self._cloud
if query_cmd := self._lua_runtime.build_query(query): if cloud and hasattr(cloud, "get_device_status"):
await self._build_send(query_cmd) if isinstance(cloud, MSmartHomeCloud):
status = await cloud.get_device_status(
def _parse_cloud_message(self, decrypted): appliance_code=self._device_id,
# MideaLogger.debug(f"Received: {decrypted}") device_type=self.device_type,
if status := self._lua_runtime.decode_status(dec_string_to_bytes(decrypted).hex()): sn=self.sn,
MideaLogger.debug(f"Decoded: {status}") model_number=self.subtype,
new_status = {} manufacturer_code=self._manufacturer_code,
for single in status.keys(): query=query
value = status.get(single) )
if single not in self._attributes or self._attributes[single] != value: else:
self._attributes[single] = value if self._lua_runtime is not None:
new_status[single] = value if query_cmd := self._lua_runtime.build_query(query):
if len(new_status) > 0:
for c in self._calculate_get:
lvalue = c.get("lvalue")
rvalue = c.get("rvalue")
if lvalue and rvalue:
calculate = False
for s, v in new_status.items():
if rvalue.find(f"[{s}]") >= 0:
calculate = True
break
if calculate:
calculate_str1 = \
(f"{lvalue.replace('[', 'self._attributes[')} = "
f"{rvalue.replace('[', 'self._attributes[')}") \
.replace("[", "[\"").replace("]", "\"]")
calculate_str2 = \
(f"{lvalue.replace('[', 'new_status[')} = "
f"{rvalue.replace('[', 'self._attributes[')}") \
.replace("[", "[\"").replace("]", "\"]")
try: try:
exec(calculate_str1) await self._build_send(query_cmd)
exec(calculate_str2) return
except Exception: except Exception as e:
MideaLogger.warning( traceback.print_exc()
f"Calculation Error: {lvalue} = {rvalue}", self._device_id
) status = await cloud.get_device_status(
self._update_all(new_status) appliance_code=self._device_id,
query=query
)
self._parse_cloud_message(status)
def _parse_cloud_message(self, status):
# MideaLogger.debug(f"Received: {decrypted}")
new_status = {}
for single in status.keys():
value = status.get(single)
if single not in self._attributes or self._attributes[single] != value:
self._attributes[single] = value
new_status[single] = value
if len(new_status) > 0:
for c in self._calculate_get:
lvalue = c.get("lvalue")
rvalue = c.get("rvalue")
if lvalue and rvalue:
calculate = False
for s, v in new_status.items():
if rvalue.find(f"[{s}]") >= 0:
calculate = True
break
if calculate:
calculate_str1 = \
(f"{lvalue.replace('[', 'self._attributes[').replace("]", "\"]")} = "
f"{rvalue.replace('[', 'float(self._attributes[').replace(']', "\"])")}") \
.replace("[", "[\"")
calculate_str2 = \
(f"{lvalue.replace('[', 'new_status[').replace("]", "\"]")} = "
f"{rvalue.replace('[', 'float(self._attributes[').replace(']', "\"])")}") \
.replace("[", "[\"")
try:
exec(calculate_str1)
exec(calculate_str2)
except Exception as e:
traceback.print_exc()
MideaLogger.warning(
f"Calculation Error: {lvalue} = {rvalue}, calculate_str1: {calculate_str1}, calculate_str2: {calculate_str2}",
self._device_id
)
self._update_all(new_status)
return ParseMessageResult.SUCCESS return ParseMessageResult.SUCCESS
def _parse_message(self, msg): def _parse_message(self, msg):
@@ -371,11 +396,13 @@ class MiedaDevice(threading.Thread):
async def _send_message(self, data): async def _send_message(self, data):
if reply := await self._cloud.send_cloud(self._device_id, data): if reply := await self._cloud.send_cloud(self._device_id, data):
result = self._parse_cloud_message(reply) if reply_dec := self._lua_runtime.decode_status(dec_string_to_bytes(reply).hex()):
if result == ParseMessageResult.ERROR: MideaLogger.debug(f"Decoded: {reply_dec}")
MideaLogger.debug(f"Message 'ERROR' received") result = self._parse_cloud_message(reply_dec)
elif result == ParseMessageResult.SUCCESS: if result == ParseMessageResult.ERROR:
timeout_counter = 0 MideaLogger.debug(f"Message 'ERROR' received")
elif result == ParseMessageResult.SUCCESS:
timeout_counter = 0
# if self._protocol == 3: # if self._protocol == 3:
# self._send_message_v3(data, msg_type=MSGTYPE_ENCRYPTED_REQUEST) # self._send_message_v3(data, msg_type=MSGTYPE_ENCRYPTED_REQUEST)

View File

@@ -1,9 +1,11 @@
"""Data coordinator for Midea Auto Cloud integration.""" """Data coordinator for Midea Auto Cloud integration."""
import logging import logging
import traceback
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import NamedTuple from typing import NamedTuple
from attr import attributes
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers.event import async_call_later from homeassistant.helpers.event import async_call_later
@@ -184,14 +186,35 @@ class MideaDataUpdateCoordinator(DataUpdateCoordinator[MideaDeviceData]):
async def async_set_attribute(self, attribute: str, value) -> None: async def async_set_attribute(self, attribute: str, value) -> None:
"""Set a device attribute.""" """Set a device attribute."""
# 云端控制:构造 control 与 status携带当前状态作为上下文 attributes = {}
await self.device.set_attribute(attribute, value) attributes[attribute] = value
self.device.attributes[attribute] = value await self.async_set_attributes(attributes)
self.mute_state_update_for_a_while()
self.async_update_listeners()
async def async_set_attributes(self, attributes: dict) -> None: async def async_set_attributes(self, attributes: dict) -> None:
"""Set multiple device attributes.""" """Set multiple device attributes."""
# 云端控制:构造 control 与 status携带当前状态作为上下文
for c in self.device._calculate_set:
lvalue = c.get("lvalue")
rvalue = c.get("rvalue")
if lvalue and rvalue:
calculate = False
for s, v in attributes.items():
if rvalue.find(f"[{s}]") >= 0:
calculate = True
break
if calculate:
calculate_str1 = \
(f"{lvalue.replace('[', 'attributes[').replace("]", "\"]")} = "
f"{rvalue.replace('[', 'float(attributes[').replace(']', "\"])")}") \
.replace("[", "[\"")
try:
exec(calculate_str1)
except Exception as e:
traceback.print_exc()
MideaLogger.warning(
f"Calculation Error: {lvalue} = {rvalue}, calculate_str1: {calculate_str1}",
self._device_id
)
await self.device.set_attributes(attributes) await self.device.set_attributes(attributes)
self.device.attributes.update(attributes) self.device.attributes.update(attributes)
self.mute_state_update_for_a_while() self.mute_state_update_for_a_while()

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

@@ -1,4 +1,5 @@
from homeassistant.const import Platform, UnitOfTemperature, PRECISION_HALVES 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.sensor import SensorStateClass, SensorDeviceClass
from homeassistant.components.switch import SwitchDeviceClass from homeassistant.components.switch import SwitchDeviceClass
@@ -107,19 +108,19 @@ DEVICE_MAPPING = {
}, },
"co2_value": { "co2_value": {
"device_class": SensorDeviceClass.CO2, "device_class": SensorDeviceClass.CO2,
"unit_of_measurement": "ppm", "unit_of_measurement": CONCENTRATION_PARTS_PER_MILLION,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
"attribute": "co2.value" "attribute": "co2.value"
}, },
"hcho_value": { "hcho_value": {
"device_class": SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, "device_class": SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
"unit_of_measurement": "μg/m³", "unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
"attribute": "hcho.value" "attribute": "hcho.value"
}, },
"pm25_value": { "pm25_value": {
"device_class": SensorDeviceClass.PM25, "device_class": SensorDeviceClass.PM25,
"unit_of_measurement": "μg/m³", "unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"state_class": SensorStateClass.MEASUREMENT, "state_class": SensorStateClass.MEASUREMENT,
"attribute": "pm2_5.value" "attribute": "pm2_5.value"
}, },

View File

@@ -0,0 +1,69 @@
from homeassistant.const import Platform, UnitOfTemperature, UnitOfTime, PERCENTAGE, PRECISION_HALVES, PRECISION_WHOLE
from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.switch import SwitchDeviceClass
DEVICE_MAPPING = {
"default": {
"manufacturer": "美的",
"rationale": ["off", "on"],
"queries": [{}],
"calculate": {
"get": [
{
"lvalue": "[temperature]",
"rvalue": "([set_temperature] - 106) / 74 * 37 + 38"
},
{
"lvalue": "[cur_temperature]",
"rvalue": "([water_box_temperature] - 106) / 74 * 37 + 38"
}
],
"set": [
{
"lvalue": "[set_temperature]",
"rvalue": "([temperature] - 38) / 37 * 74 + 106"
},
]
},
"centralized": [],
"entities": {
Platform.CLIMATE: {
"water_heater": {
"power": "power",
"hvac_modes": {
"off": {"power": "off"},
"heat": {"power": "on"},
},
"preset_modes": {
"标准": {"mode": "standard"},
"节能": {"mode": "energy"},
"速热": {"mode": "compatibilizing"},
},
"target_temperature": "temperature",
"current_temperature": "cur_temperature",
"min_temp": 38,
"max_temp": 75,
"temperature_unit": UnitOfTemperature.CELSIUS,
"precision": PRECISION_WHOLE,
}
},
Platform.SWITCH: {
"power": {
"device_class": SwitchDeviceClass.SWITCH,
},
"mute": {
"device_class": SwitchDeviceClass.SWITCH,
},
},
Platform.SENSOR: {
"cur_temperature": {
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT
},
},
}
}
}

View File

@@ -1,4 +1,5 @@
from homeassistant.const import Platform, UnitOfTemperature, UnitOfTime from homeassistant.const import Platform, UnitOfTemperature, UnitOfTime, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, \
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
@@ -168,12 +169,12 @@ DEVICE_MAPPING = {
}, },
"pm25_value": { "pm25_value": {
"device_class": SensorDeviceClass.PM25, "device_class": SensorDeviceClass.PM25,
"unit_of_measurement": "µg/m³", "unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"state_class": SensorStateClass.MEASUREMENT "state_class": SensorStateClass.MEASUREMENT
}, },
"co2_value": { "co2_value": {
"device_class": SensorDeviceClass.CO2, "device_class": SensorDeviceClass.CO2,
"unit_of_measurement": "ppm", "unit_of_measurement": CONCENTRATION_PARTS_PER_MILLION,
"state_class": SensorStateClass.MEASUREMENT "state_class": SensorStateClass.MEASUREMENT
}, },
"machine_type": { "machine_type": {

View File

@@ -1,4 +1,5 @@
from homeassistant.const import Platform, UnitOfTemperature, UnitOfTime, PERCENTAGE, PRECISION_HALVES from homeassistant.const import Platform, UnitOfTemperature, UnitOfTime, PERCENTAGE, PRECISION_HALVES, \
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
@@ -10,12 +11,23 @@ DEVICE_MAPPING = {
"queries": [{}], "queries": [{}],
"centralized": [], "centralized": [],
"entities": { "entities": {
Platform.NUMBER: {
"water_quality": {
"min": 0,
"max": 3,
"step": 1
},
"cur_temperature": {
"device_class": SensorDeviceClass.TEMPERATURE,
"state_class": SensorStateClass.MEASUREMENT,
}
},
Platform.CLIMATE: { Platform.CLIMATE: {
"water_heater": { "water_heater": {
"power": "power", "power": "power",
"hvac_modes": { "hvac_modes": {
"off": {"power": "off"}, "off": {"power": "off"},
"on": {"power": "on"}, "heat": {"power": "on"},
}, },
"target_temperature": "temperature", "target_temperature": "temperature",
"current_temperature": "cur_temperature", "current_temperature": "cur_temperature",
@@ -52,14 +64,6 @@ DEVICE_MAPPING = {
}, },
}, },
Platform.SELECT: { Platform.SELECT: {
"water_quality": {
"options": {
"0": {"water_quality": 0},
"1": {"water_quality": 1},
"2": {"water_quality": 2},
"3": {"water_quality": 3}
}
},
"func_select": { "func_select": {
"options": { "options": {
"low": {"func_select": "low"}, "low": {"func_select": "low"},
@@ -152,7 +156,7 @@ DEVICE_MAPPING = {
}, },
"tds_value": { "tds_value": {
"device_class": SensorDeviceClass.WATER, "device_class": SensorDeviceClass.WATER,
"unit_of_measurement": "ppm", "unit_of_measurement": CONCENTRATION_PARTS_PER_MILLION,
"state_class": SensorStateClass.MEASUREMENT "state_class": SensorStateClass.MEASUREMENT
}, },
"heat_water_level": { "heat_water_level": {

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

@@ -1,4 +1,5 @@
from homeassistant.const import Platform, UnitOfTemperature, UnitOfTime, PERCENTAGE, DEGREE from homeassistant.const import Platform, UnitOfTemperature, UnitOfTime, PERCENTAGE, DEGREE, \
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
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
@@ -94,7 +95,7 @@ DEVICE_MAPPING = {
}, },
"pm25": { "pm25": {
"device_class": SensorDeviceClass.PM25, "device_class": SensorDeviceClass.PM25,
"unit_of_measurement": "µg/m³", "unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"state_class": SensorStateClass.MEASUREMENT "state_class": SensorStateClass.MEASUREMENT
}, },
"ud_swing_angle": { "ud_swing_angle": {

View File

@@ -0,0 +1,103 @@
from homeassistant.const import Platform, UnitOfTemperature, UnitOfVolume, UnitOfTime, PERCENTAGE, PRECISION_HALVES, \
UnitOfEnergy, UnitOfPower, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.switch import SwitchDeviceClass
DEVICE_MAPPING = {
"default": {
"rationale": ["off", "on"],
"queries": [{}],
"centralized": [],
"entities": {
Platform.SWITCH: {
"power": {
"device_class": SwitchDeviceClass.SWITCH,
},
"anion": {
"device_class": SwitchDeviceClass.SWITCH,
},
"buzzer": {
"device_class": SwitchDeviceClass.SWITCH,
},
"lock": {
"device_class": SwitchDeviceClass.SWITCH,
},
"waterions":{
"device_class": SwitchDeviceClass.SWITCH,
}
},
Platform.SELECT: {
"mode": {
"options": {
"constant_humidity": {"mode": "constant_humidity"},
"manual": {"mode": "manual"},
"sleep": {"mode": "sleep"},
"fast": {"mode": "fast"},
"auto": {"mode": "auto"},
}
},
"bias_gear":{
"options": {
"瑜伽静修场景": {"mode": "auto", "sub_mode": "denoise", "bias_gear": -20},
"室内对话场景": {"mode": "auto", "sub_mode": "denoise", "bias_gear": -10}
}
},
"bright": {
"options": {
"全亮": {"bright": 0},
"半亮": {"bright": 6},
"熄灭": {"bright": 7}
}
},
"gear": {
"options": {
"low": {"wind_speed": 1},
"medium": {"wind_speed": 2},
"high": {"wind_speed": 3}
}
},
"humidity": {
"options": {
"40%": {"humidity": 40},
"50%": {"humidity": 50},
"60%": {"humidity": 60},
"70%": {"humidity": 70}
}
},
},
Platform.SENSOR: {
"temperature_feedback": {
"device_class": SensorDeviceClass.TEMPERATURE,
"unit_of_measurement": UnitOfTemperature.CELSIUS,
"state_class": SensorStateClass.MEASUREMENT
},
"humidify_feedback": {
"device_class": SensorDeviceClass.HUMIDITY,
"unit_of_measurement": "%",
"state_class": SensorStateClass.MEASUREMENT
},
"hcho":{
"device_class": SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
"unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"state_class": SensorStateClass.MEASUREMENT
},
"pm1":{
"device_class": SensorDeviceClass.PM1,
"unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"state_class": SensorStateClass.MEASUREMENT
},
"pm25":{
"device_class": SensorDeviceClass.PM25,
"unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"state_class": SensorStateClass.MEASUREMENT
},
"pm10":{
"device_class": SensorDeviceClass.PM10,
"unit_of_measurement": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"state_class": SensorStateClass.MEASUREMENT
}
}
}
}
}

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.23"
} }

View File

@@ -349,6 +349,15 @@
} }
}, },
"select": { "select": {
"bright": {
"name": "Brightness"
},
"bias_gear": {
"name": "Bias Gear"
},
"humidity": {
"name": "Humidity"
},
"b6_power": { "b6_power": {
"name": "Smoke Machine Power" "name": "Smoke Machine Power"
}, },
@@ -553,9 +562,6 @@
"type_select": { "type_select": {
"name": "Type Select" "name": "Type Select"
}, },
"water_quality": {
"name": "Water Quality"
},
"work_status": { "work_status": {
"name": "Work Status" "name": "Work Status"
}, },
@@ -1437,6 +1443,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"
}, },
@@ -1510,6 +1519,9 @@
} }
}, },
"number": { "number": {
"water_quality": {
"name": "Water Quality"
},
"b6_lightness": { "b6_lightness": {
"name": "Smoke Machine Lightness" "name": "Smoke Machine Lightness"
}, },
@@ -1562,6 +1574,12 @@
} }
}, },
"switch": { "switch": {
"waterions": {
"name": "Disinfection"
},
"mute": {
"name": "Mute"
},
"b6_light": { "b6_light": {
"name": "Smoke Machine Light" "name": "Smoke Machine Light"
}, },
@@ -1812,7 +1830,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

@@ -353,6 +353,15 @@
} }
}, },
"select": { "select": {
"bright": {
"name": "亮度"
},
"bias_gear": {
"name": "降噪设置"
},
"humidity": {
"name": "设定湿度"
},
"b6_power": { "b6_power": {
"name": "烟机开关" "name": "烟机开关"
}, },
@@ -560,9 +569,6 @@
"type_select": { "type_select": {
"name": "类型选择" "name": "类型选择"
}, },
"water_quality": {
"name": "水质"
},
"work_status": { "work_status": {
"name": "工作状态" "name": "工作状态"
}, },
@@ -1441,6 +1447,9 @@
"left_status": { "left_status": {
"name": "左侧状态" "name": "左侧状态"
}, },
"right_status": {
"name": "右侧状态"
},
"eq_recipe_action": { "eq_recipe_action": {
"name": "等量食谱动作" "name": "等量食谱动作"
}, },
@@ -1514,6 +1523,9 @@
} }
}, },
"number": { "number": {
"water_quality": {
"name": "水质"
},
"b6_lightness": { "b6_lightness": {
"name": "烟机照明" "name": "烟机照明"
}, },
@@ -1566,6 +1578,12 @@
} }
}, },
"switch": { "switch": {
"waterions": {
"name": "消杀"
},
"mute": {
"name": "静音"
},
"b6_light": { "b6_light": {
"name": "烟机灯" "name": "烟机灯"
}, },
@@ -1816,7 +1834,7 @@
"name": "CO2检测启用" "name": "CO2检测启用"
}, },
"cold_water": { "cold_water": {
"name": "零冷水" "name": "零冷水单次循环"
}, },
"cold_water_ai": { "cold_water_ai": {
"name": "AI零冷水" "name": "AI零冷水"