Add online status

This commit is contained in:
sususweet
2025-09-12 00:15:14 +08:00
parent 94c131613c
commit 85365338f4
16 changed files with 350 additions and 631 deletions

View File

@@ -9,6 +9,7 @@ try:
except ImportError: except ImportError:
from homeassistant.util.json import save_json from homeassistant.util.json import save_json
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.core import ( from homeassistant.core import (
HomeAssistant, HomeAssistant,
ServiceCall ServiceCall
@@ -26,9 +27,11 @@ from homeassistant.const import (
CONF_DEVICE, CONF_DEVICE,
CONF_ENTITIES CONF_ENTITIES
) )
from .core.logger import MideaLogger from .core.logger import MideaLogger
from .core.device import MiedaDevice from .core.device import MiedaDevice
from .data_coordinator import MideaDataUpdateCoordinator from .data_coordinator import MideaDataUpdateCoordinator
from .core.cloud import get_midea_cloud
from .const import ( from .const import (
DOMAIN, DOMAIN,
DEVICES, DEVICES,
@@ -39,20 +42,21 @@ from .const import (
CONF_SN8, CONF_SN8,
CONF_SN, CONF_SN,
CONF_MODEL_NUMBER, CONF_MODEL_NUMBER,
CONF_LUA_FILE CONF_LUA_FILE, CONF_SERVERS
) )
# 账号型:登录云端、获取设备列表,并为每台设备建立协调器(无本地控制)
from .const import CONF_PASSWORD as CONF_PASSWORD_KEY, CONF_SERVER as CONF_SERVER_KEY
ALL_PLATFORM = [ PLATFORMS: list[Platform] = [
Platform.BINARY_SENSOR, Platform.BINARY_SENSOR,
Platform.SENSOR, # Platform.SENSOR,
Platform.SWITCH, # Platform.SWITCH,
Platform.CLIMATE, Platform.CLIMATE,
Platform.SELECT, Platform.SELECT,
Platform.WATER_HEATER, Platform.WATER_HEATER,
Platform.FAN Platform.FAN
] ]
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)
count = 0 count = 0
@@ -175,83 +179,87 @@ async def async_setup(hass: HomeAssistant, config: ConfigType):
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry): async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
device_type = config_entry.data.get(CONF_TYPE) device_type = config_entry.data.get(CONF_TYPE)
MideaLogger.debug(f"async_setup_entry type={device_type} data={config_entry.data}")
if device_type == CONF_ACCOUNT: if device_type == CONF_ACCOUNT:
return True account = config_entry.data.get(CONF_ACCOUNT)
name = config_entry.data.get(CONF_NAME) password = config_entry.data.get(CONF_PASSWORD_KEY)
device_id = config_entry.data.get(CONF_DEVICE_ID) server = config_entry.data.get(CONF_SERVER_KEY)
device_type = config_entry.data.get(CONF_TYPE) cloud_name = CONF_SERVERS.get(server)
token = config_entry.data.get(CONF_TOKEN) cloud = get_midea_cloud(
key = config_entry.data.get(CONF_KEY) cloud_name=cloud_name,
ip_address = config_entry.options.get(CONF_IP_ADDRESS, None) session=async_get_clientsession(hass),
if not ip_address: account=account,
ip_address = config_entry.data.get(CONF_IP_ADDRESS) password=password,
refresh_interval = config_entry.options.get(CONF_REFRESH_INTERVAL)
port = config_entry.data.get(CONF_PORT)
model = config_entry.data.get(CONF_MODEL)
protocol = config_entry.data.get(CONF_PROTOCOL)
subtype = config_entry.data.get(CONF_MODEL_NUMBER)
sn = config_entry.data.get(CONF_SN)
sn8 = config_entry.data.get(CONF_SN8)
lua_file = config_entry.data.get(CONF_LUA_FILE)
if protocol == 3 and (key is None or key is None):
MideaLogger.error("For V3 devices, the key and the token is required.")
return False
device = MiedaDevice(
name=name,
device_id=device_id,
device_type=device_type,
ip_address=ip_address,
port=port,
token=token,
key=key,
protocol=protocol,
model=model,
subtype=subtype,
sn=sn,
sn8=sn8,
lua_file=lua_file,
) )
if refresh_interval is not None: if not cloud or not await cloud.login():
device.set_refresh_interval(refresh_interval) MideaLogger.error("Midea cloud login failed")
device.open() return False
if DOMAIN not in hass.data:
hass.data[DOMAIN] = {}
if DEVICES not in hass.data[DOMAIN]:
hass.data[DOMAIN][DEVICES] = {}
# Create data coordinator # 拉取家庭与设备列表
appliances = None
try:
homes = await cloud.list_home()
if homes and len(homes) > 0:
first_home_id = list(homes.keys())[0]
appliances = await cloud.list_appliances(first_home_id)
else:
appliances = await cloud.list_appliances(None)
except Exception as e:
MideaLogger.error(f"Fetch appliances failed: {e}")
appliances = None
if appliances is None:
appliances = {}
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN].setdefault("accounts", {})
bucket = {"device_list": {}, "coordinator_map": {}}
# 为每台设备构建占位设备与协调器(不连接本地)
for appliance_code, info in appliances.items():
MideaLogger.debug(f"info={info} ")
try:
device = MiedaDevice(
name=info.get(CONF_NAME) or info.get("name"),
device_id=appliance_code,
device_type=info.get(CONF_TYPE) or info.get("type"),
ip_address=None,
port=None,
token=None,
key=None,
connected=info.get("online"),
protocol=info.get(CONF_PROTOCOL) or 2,
model=info.get(CONF_MODEL),
subtype=info.get(CONF_MODEL_NUMBER),
sn=info.get(CONF_SN) or info.get("sn"),
sn8=info.get(CONF_SN8) or info.get("sn8"),
lua_file=None,
)
coordinator = MideaDataUpdateCoordinator(hass, config_entry, device) coordinator = MideaDataUpdateCoordinator(hass, config_entry, device)
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
bucket["device_list"][appliance_code] = info
bucket["coordinator_map"][appliance_code] = coordinator
except Exception as e:
MideaLogger.error(f"Init device failed: {appliance_code}, error: {e}")
hass.data[DOMAIN][DEVICES][device_id] = {} hass.data[DOMAIN]["accounts"][config_entry.entry_id] = bucket
hass.data[DOMAIN][DEVICES][device_id][CONF_DEVICE] = device
hass.data[DOMAIN][DEVICES][device_id]["coordinator"] = coordinator
hass.data[DOMAIN][DEVICES][device_id][CONF_ENTITIES] = {}
config = load_device_config(hass, device_type, sn8) hass.async_create_task(hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS))
if config is not None and len(config) > 0:
queries = config.get("queries")
if queries is not None and isinstance(queries, list):
device.set_queries(queries)
centralized = config.get("centralized")
if centralized is not None and isinstance(centralized, list):
device.set_centralized(centralized)
calculate = config.get("calculate")
if calculate is not None and isinstance(calculate, dict):
device.set_calculate(calculate)
hass.data[DOMAIN][DEVICES][device_id]["manufacturer"] = config.get("manufacturer")
hass.data[DOMAIN][DEVICES][device_id]["rationale"] = config.get("rationale")
hass.data[DOMAIN][DEVICES][device_id][CONF_ENTITIES] = config.get(CONF_ENTITIES)
for platform in ALL_PLATFORM:
hass.async_create_task(hass.config_entries.async_forward_entry_setup(
config_entry, platform))
config_entry.add_update_listener(update_listener)
return True return True
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry): async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry):
device_id = config_entry.data.get(CONF_DEVICE_ID) device_id = config_entry.data.get(CONF_DEVICE_ID)
device_type = config_entry.data.get(CONF_TYPE)
if device_type == CONF_ACCOUNT:
# 卸载平台并清理账号桶
unload_ok = await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
if unload_ok:
try:
hass.data.get(DOMAIN, {}).get("accounts", {}).pop(config_entry.entry_id, None)
except Exception:
pass
return unload_ok
if device_id is not None: if device_id is not None:
device: MiedaDevice = hass.data[DOMAIN][DEVICES][device_id][CONF_DEVICE] device: MiedaDevice = hass.data[DOMAIN][DEVICES][device_id][CONF_DEVICE]
if device is not None: if device is not None:
@@ -259,7 +267,7 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry):
lua_file = config_entry.data.get("lua_file") lua_file = config_entry.data.get("lua_file")
os.remove(lua_file) os.remove(lua_file)
remove_device_config(hass, device.sn8) remove_device_config(hass, device.sn8)
device.close() # device.close()
hass.data[DOMAIN][DEVICES].pop(device_id) hass.data[DOMAIN][DEVICES].pop(device_id)
for platform in ALL_PLATFORM: for platform in ALL_PLATFORM:
await hass.config_entries.async_forward_entry_unload(config_entry, platform) await hass.config_entries.async_forward_entry_unload(config_entry, platform)

View File

@@ -2,20 +2,14 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity, BinarySensorEntity,
BinarySensorDeviceClass BinarySensorDeviceClass
) )
from homeassistant.const import ( from homeassistant.const import Platform
Platform,
CONF_DEVICE_ID,
CONF_ENTITIES, CONF_DEVICE
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ( from .const import DOMAIN
DOMAIN,
DEVICES
)
from .midea_entity import MideaEntity from .midea_entity import MideaEntity
from . import load_device_config
async def async_setup_entry( async def async_setup_entry(
@@ -24,19 +18,29 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up binary sensor entities for Midea devices.""" """Set up binary sensor entities for Midea devices."""
device_id = config_entry.data.get(CONF_DEVICE_ID) account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
device_data = hass.data[DOMAIN][DEVICES][device_id] if not account_bucket:
coordinator = device_data.get("coordinator") async_add_entities([])
device = device_data.get(CONF_DEVICE) return
manufacturer = device_data.get("manufacturer") device_list = account_bucket.get("device_list", {})
rationale = device_data.get("rationale") coordinator_map = account_bucket.get("coordinator_map", {})
entities = device_data.get(CONF_ENTITIES, {}).get(Platform.BINARY_SENSOR, {})
devs = [MideaDeviceStatusSensorEntity(coordinator, device, manufacturer, rationale, "Status", {})] devs = []
if entities: for device_id, info in device_list.items():
for entity_key, config in entities.items(): device_type = info.get("type")
sn8 = info.get("sn8")
config = load_device_config(hass, device_type, sn8) or {}
entities_cfg = (config.get("entities") or {}).get(Platform.BINARY_SENSOR, {})
manufacturer = config.get("manufacturer")
rationale = config.get("rationale")
coordinator = coordinator_map.get(device_id)
device = coordinator.device if coordinator else None
# 连接状态实体
if coordinator and device:
devs.append(MideaDeviceStatusSensorEntity(coordinator, device, manufacturer, rationale, "Status", {}))
for entity_key, ecfg in entities_cfg.items():
devs.append(MideaBinarySensorEntity( devs.append(MideaBinarySensorEntity(
coordinator, device, manufacturer, rationale, entity_key, config coordinator, device, manufacturer, rationale, entity_key, ecfg
)) ))
async_add_entities(devs) async_add_entities(devs)

View File

@@ -6,20 +6,16 @@ from homeassistant.components.climate import (
) )
from homeassistant.const import ( from homeassistant.const import (
Platform, Platform,
CONF_DEVICE_ID, ATTR_TEMPERATURE,
CONF_ENTITIES,
ATTR_TEMPERATURE, CONF_DEVICE
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ( from .const import DOMAIN
DOMAIN,
DEVICES
)
from .midea_entity import MideaEntity from .midea_entity import MideaEntity
from .midea_entities import Rationale from .midea_entities import Rationale
from . import load_device_config
async def async_setup_entry( async def async_setup_entry(
@@ -28,19 +24,27 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up climate entities for Midea devices.""" """Set up climate entities for Midea devices."""
device_id = config_entry.data.get(CONF_DEVICE_ID) # 账号型 entry从 __init__ 写入的 accounts 桶加载设备和协调器
device_data = hass.data[DOMAIN][DEVICES][device_id] account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
coordinator = device_data.get("coordinator") if not account_bucket:
device = device_data.get(CONF_DEVICE) async_add_entities([])
manufacturer = device_data.get("manufacturer") return
rationale = device_data.get("rationale") device_list = account_bucket.get("device_list", {})
entities = device_data.get(CONF_ENTITIES, {}).get(Platform.CLIMATE, {}) coordinator_map = account_bucket.get("coordinator_map", {})
devs = [] devs = []
if entities: for device_id, info in device_list.items():
for entity_key, config in entities.items(): device_type = info.get("type")
sn8 = info.get("sn8")
config = load_device_config(hass, device_type, sn8) or {}
entities_cfg = (config.get("entities") or {}).get(Platform.CLIMATE, {})
manufacturer = config.get("manufacturer")
rationale = config.get("rationale")
coordinator = coordinator_map.get(device_id)
device = coordinator.device if coordinator else None
for entity_key, ecfg in entities_cfg.items():
devs.append(MideaClimateEntity( devs.append(MideaClimateEntity(
coordinator, device, manufacturer, rationale, entity_key, config coordinator, device, manufacturer, rationale, entity_key, ecfg
)) ))
async_add_entities(devs) async_add_entities(devs)
@@ -81,8 +85,8 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
features |= ClimateEntityFeature.TARGET_TEMPERATURE features |= ClimateEntityFeature.TARGET_TEMPERATURE
if self._key_preset_modes is not None: if self._key_preset_modes is not None:
features |= ClimateEntityFeature.PRESET_MODE features |= ClimateEntityFeature.PRESET_MODE
if self._key_aux_heat is not None: # if self._key_aux_heat is not None:
features |= ClimateEntityFeature.AUX_HEAT # features |= ClimateEntityFeature.AUX_HEAT
if self._key_swing_modes is not None: if self._key_swing_modes is not None:
features |= ClimateEntityFeature.SWING_MODE features |= ClimateEntityFeature.SWING_MODE
if self._key_fan_modes is not None: if self._key_fan_modes is not None:
@@ -227,7 +231,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
return return
await self.async_set_attribute(key, value) await self.async_set_attribute(key, value)
def _dict_get_selected(self, dict_config, rationale=Rationale.EQUAL): def _dict_get_selected(self, dict_config, rationale=Rationale.EQUALLY):
"""Get selected value from dictionary configuration.""" """Get selected value from dictionary configuration."""
if dict_config is None: if dict_config is None:
return None return None
@@ -238,7 +242,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
match = True match = True
for attr_key, attr_value in config.items(): for attr_key, attr_value in config.items():
device_value = self.device_attributes.get(attr_key) device_value = self.device_attributes.get(attr_key)
if rationale == Rationale.EQUAL: if rationale == Rationale.EQUALLY:
if device_value != attr_value: if device_value != attr_value:
match = False match = False
break break

View File

@@ -1,293 +1,68 @@
import voluptuous as vol import voluptuous as vol
import logging import logging
import os from typing import Any
import ipaddress
from homeassistant.helpers.aiohttp_client import async_create_clientsession from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.config_entries import ConfigFlowResult
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.const import ( from homeassistant.const import (
CONF_TYPE, CONF_TYPE,
CONF_PASSWORD,
CONF_PORT,
CONF_MODEL,
CONF_IP_ADDRESS,
CONF_DEVICE_ID,
CONF_PROTOCOL,
CONF_TOKEN,
CONF_NAME
) )
from . import remove_device_config, load_device_config
from .core.cloud import get_midea_cloud
from .core.discover import discover
from .core.device import MiedaDevice
from .const import ( from .const import (
DOMAIN,
CONF_REFRESH_INTERVAL,
STORAGE_PATH,
CONF_ACCOUNT, CONF_ACCOUNT,
CONF_SERVER, CONF_PASSWORD,
CONF_HOME, DOMAIN,
CONF_KEY, CONF_SERVER, CONF_SERVERS
CONF_SN8,
CONF_SN,
CONF_MODEL_NUMBER,
CONF_LUA_FILE
) )
from .core.cloud import get_midea_cloud
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
servers = {
1: "MSmartHome",
2: "美的美居",
}
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
_session = None _session = None
_cloud = None
_current_home = None
_device_list = {}
_device = None
@staticmethod @staticmethod
@callback @callback
def async_get_options_flow(config_entry): def async_get_options_flow(config_entry):
return OptionsFlowHandler(config_entry) return OptionsFlowHandler(config_entry)
def _get_configured_account(self): async def async_step_user(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
for entry in self._async_current_entries(): errors: dict[str, str] = {}
if entry.data.get(CONF_TYPE) == CONF_ACCOUNT:
return entry.data.get(CONF_ACCOUNT), entry.data.get(CONF_PASSWORD), entry.data.get(CONF_SERVER)
return None, None, None
def _device_configured(self, device_id):
for entry in self._async_current_entries():
if device_id == entry.data.get(CONF_DEVICE_ID):
return True
return False
@staticmethod
def _is_valid_ip_address(ip_address):
try:
ipaddress.ip_address(ip_address)
return True
except ValueError:
return False
async def async_step_user(self, user_input=None, error=None):
if self._session is None: if self._session is None:
self._session = async_create_clientsession(self.hass) self._session = async_create_clientsession(self.hass)
account, password, server = self._get_configured_account()
if account is not None and password is not None:
if self._cloud is None:
self._cloud = get_midea_cloud(
session=self._session,
cloud_name=servers[server],
account=account,
password=password
)
try:
if await self._cloud.login():
return await self.async_step_home()
else:
return await self.async_step_user(error="account_invalid")
except Exception as e:
_LOGGER.error(f"Login error: {e}")
return await self.async_step_user(error="login_failed")
if user_input is not None: if user_input is not None:
if self._cloud is None: cloud = get_midea_cloud(
self._cloud = get_midea_cloud(
session=self._session, session=self._session,
cloud_name=servers[user_input[CONF_SERVER]], cloud_name=CONF_SERVERS[user_input[CONF_SERVER]],
account=user_input[CONF_ACCOUNT], account=user_input[CONF_ACCOUNT],
password=user_input[CONF_PASSWORD] password=user_input[CONF_PASSWORD]
) )
try: try:
if await self._cloud.login(): 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( return self.async_create_entry(
title=f"{user_input[CONF_ACCOUNT]}", title=user_input[CONF_ACCOUNT],
data={ data={
CONF_TYPE: CONF_ACCOUNT, CONF_TYPE: CONF_ACCOUNT,
CONF_ACCOUNT: user_input[CONF_ACCOUNT], CONF_ACCOUNT: user_input[CONF_ACCOUNT],
CONF_PASSWORD: user_input[CONF_PASSWORD], CONF_PASSWORD: user_input[CONF_PASSWORD],
CONF_SERVER: user_input[CONF_SERVER] CONF_SERVER: user_input[CONF_SERVER]
}) },
)
else: else:
self._cloud = None errors["base"] = "login_failed"
return await self.async_step_user(error="login_failed")
except Exception as e: except Exception as e:
_LOGGER.error(f"Login error: {e}") _LOGGER.exception("Login error: %s", e)
self._cloud = None errors["base"] = "login_failed"
return await self.async_step_user(error="login_failed")
return self.async_show_form( return self.async_show_form(
step_id="user", step_id="user",
data_schema=vol.Schema({ data_schema=vol.Schema({
vol.Required(CONF_ACCOUNT): str, vol.Required(CONF_ACCOUNT): str,
vol.Required(CONF_PASSWORD): str, vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_SERVER, default=1): vol.In(servers) vol.Required(CONF_SERVER, default=1): vol.In(CONF_SERVERS)
}), }),
errors={"base": error} if error else None errors=errors,
)
async def async_step_home(self, user_input=None, error=None):
if user_input is not None:
self._current_home = user_input[CONF_HOME]
return await self.async_step_device()
homes = await self._cloud.list_home()
if homes is None or len(homes) == 0:
return await self.async_step_device(error="no_home")
return self.async_show_form(
step_id="home",
data_schema=vol.Schema({
vol.Required(CONF_HOME, default=list(homes.keys())[0]):
vol.In(homes),
}),
errors={"base": error} if error else None
)
async def async_step_device(self, user_input=None, error=None):
if user_input is not None:
# 下载lua
# 本地尝试连接设备
self._device = self._device_list[user_input[CONF_DEVICE_ID]]
if self._device.get("online") is not True:
return await self.async_step_device(error="offline_error")
return await self.async_step_discover()
appliances = await self._cloud.list_appliances(self._current_home)
self._device_list = {}
device_list = {}
for appliance_code, appliance_info in appliances.items():
if not self._device_configured(appliance_code):
try:
model_number = int(appliance_info.get("model_number")) if appliance_info.get("model_number") is not None else 0
except ValueError:
model_number = 0
self._device_list[appliance_code] = {
CONF_DEVICE_ID: appliance_code,
CONF_NAME: appliance_info.get("name"),
CONF_TYPE: appliance_info.get("type"),
CONF_SN8: appliance_info.get("sn8", "00000000"),
CONF_SN: appliance_info.get("sn"),
CONF_MODEL: appliance_info.get("model", "0"),
CONF_MODEL_NUMBER: model_number,
"manufacturer_code": appliance_info.get("manufacturer_code","0000"),
"online": appliance_info.get("online")
}
device_list[appliance_code] = \
f"{appliance_info.get('name')} ({'online' if appliance_info.get('online') is True else 'offline'})"
if len(self._device_list) == 0:
return await self.async_step_device(error="no_new_devices")
return self.async_show_form(
step_id="device",
data_schema=vol.Schema({
vol.Required(CONF_DEVICE_ID, default=list(device_list.keys())[0]):
vol.In(device_list),
}),
errors={"base": error} if error else None
)
async def async_step_discover(self, user_input=None, error=None):
if user_input is not None:
if user_input[CONF_IP_ADDRESS] == "auto" or self._is_valid_ip_address(user_input[CONF_IP_ADDRESS]):
ip_address = None
if self._is_valid_ip_address(user_input[CONF_IP_ADDRESS]):
ip_address = user_input[CONF_IP_ADDRESS]
discover_devices = discover([self._device[CONF_TYPE]], ip_address)
_LOGGER.debug(discover_devices)
if discover_devices is None or len(discover_devices) == 0:
return await self.async_step_discover(error="discover_failed")
current_device = discover_devices.get(self._device[CONF_DEVICE_ID])
if current_device is None:
return await self.async_step_discover(error="discover_failed")
os.makedirs(self.hass.config.path(STORAGE_PATH), exist_ok=True)
path = self.hass.config.path(STORAGE_PATH)
file = await self._cloud.download_lua(
path=path,
device_type=self._device[CONF_TYPE],
sn=self._device[CONF_SN],
model_number=self._device[CONF_MODEL_NUMBER],
manufacturer_code=self._device["manufacturer_code"]
)
if file is None:
return await self.async_step_discover(error="download_lua_failed")
use_token = None
use_key = None
connected = False
if current_device.get(CONF_PROTOCOL) == 3:
keys = await self._cloud.get_keys(self._device.get(CONF_DEVICE_ID))
for method, key in keys.items():
dm = MiedaDevice(
name="",
device_id=self._device.get(CONF_DEVICE_ID),
device_type=current_device.get(CONF_TYPE),
ip_address=current_device.get(CONF_IP_ADDRESS),
port=current_device.get(CONF_PORT),
token=key["token"],
key=key["key"],
protocol=3,
model=None,
subtype = None,
sn=None,
sn8=None,
lua_file=None
)
_LOGGER.debug(
f"Successful to take token and key, token: {key['token']},"
f" key: { key['key']}, method: {method}"
)
if dm.connect():
use_token = key["token"]
use_key = key["key"]
dm.disconnect()
connected = True
break
else:
dm = MiedaDevice(
name=self._device.get("name"),
device_id=self._device.get("device_id"),
device_type=current_device.get(CONF_TYPE),
ip_address=current_device.get(CONF_IP_ADDRESS),
port=current_device.get(CONF_PORT),
token=None,
key=None,
protocol=2,
model=None,
subtype=None,
sn=None,
sn8=None,
lua_file=None
)
if dm.connect():
dm.disconnect()
connected = True
if not connected:
return await self.async_step_discover(error="connect_error")
return self.async_create_entry(
title=self._device.get("name"),
data={
CONF_NAME: self._device.get(CONF_NAME),
CONF_DEVICE_ID: self._device.get(CONF_DEVICE_ID),
CONF_TYPE: current_device.get(CONF_TYPE),
CONF_PROTOCOL: current_device.get(CONF_PROTOCOL),
CONF_IP_ADDRESS: current_device.get(CONF_IP_ADDRESS),
CONF_PORT: current_device.get(CONF_PORT),
CONF_TOKEN: use_token,
CONF_KEY: use_key,
CONF_MODEL: self._device.get(CONF_MODEL),
CONF_MODEL_NUMBER: self._device.get(CONF_MODEL_NUMBER),
CONF_SN: self._device.get(CONF_SN),
CONF_SN8: self._device.get(CONF_SN8),
CONF_LUA_FILE: file,
})
else:
return await self.async_step_discover(error="invalid_input")
return self.async_show_form(
step_id="discover",
data_schema=vol.Schema({
vol.Required(CONF_IP_ADDRESS, default="auto"): str
}),
errors={"base": error} if error else None
) )
@@ -296,64 +71,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
self._config_entry = config_entry self._config_entry = config_entry
async def async_step_init(self, user_input=None, error=None): async def async_step_init(self, user_input=None, error=None):
if self._config_entry.data.get(CONF_TYPE) == CONF_ACCOUNT: # 账号型条目不支持配置项
return self.async_abort(reason="account_unsupport_config") return self.async_abort(reason="account_unsupport_config")
if user_input is not None: # 不再提供任何可配置项
if user_input.get("option") == 1: return self.async_abort(reason="account_unsupport_config")
return await self.async_step_configure() # 不提供 reset/configure 等选项步骤
else:
return await self.async_step_reset()
return self.async_show_form(
step_id="init",
data_schema=vol.Schema({
vol.Required("option", default=1):
vol.In({1: "Options", 2: "Reset device configuration"})
}),
errors={"base": error} if error else None
)
async def async_step_reset(self, user_input=None):
if user_input is not None:
if user_input["check"]:
remove_device_config(self.hass, self._config_entry.data.get(CONF_SN8))
load_device_config(
self.hass,
self._config_entry.data.get(CONF_TYPE),
self._config_entry.data.get(CONF_SN8))
return self.async_abort(reason="reset_success")
return self.async_show_form(
step_id="reset",
data_schema=vol.Schema({
vol.Required("check", default=False): bool
})
)
async def async_step_configure(self, user_input=None):
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
ip_address = self._config_entry.options.get(
CONF_IP_ADDRESS, None
)
if ip_address is None:
ip_address = self._config_entry.data.get(
CONF_IP_ADDRESS, None
)
refresh_interval = self._config_entry.options.get(
CONF_REFRESH_INTERVAL, 30
)
data_schema = vol.Schema({
vol.Required(
CONF_IP_ADDRESS,
default=ip_address
): str,
vol.Required(
CONF_REFRESH_INTERVAL,
default=refresh_interval
): int
})
return self.async_show_form(
step_id="configure",
data_schema=data_schema
)

File diff suppressed because one or more lines are too long

View File

@@ -96,9 +96,10 @@ class MideaCloud:
break break
except Exception as e: except Exception as e:
pass pass
print(response)
if int(response["code"]) == 0 and "data" in response: if int(response["code"]) == 0 and "data" in response:
return response["data"] return response["data"]
print(response)
return None return None
async def _get_login_id(self) -> str | None: async def _get_login_id(self) -> str | None:

View File

@@ -32,13 +32,14 @@ class MiedaDevice(threading.Thread):
name: str, name: str,
device_id: int, device_id: int,
device_type: int, device_type: int,
ip_address: str, ip_address: str | None,
port: int, port: int | None,
token: str | None, token: str | None,
key: str | None, key: str | None,
protocol: int, protocol: int,
model: str | None, model: str | None,
subtype: int | None, subtype: int | None,
connected: bool,
sn: str | None, sn: str | None,
sn8: str | None, sn8: str | None,
lua_file: str | None): lua_file: str | None):
@@ -68,7 +69,7 @@ class MiedaDevice(threading.Thread):
} }
self._refresh_interval = 30 self._refresh_interval = 30
self._heartbeat_interval = 10 self._heartbeat_interval = 10
self._connected = False self._device_connected(connected)
self._queries = [{}] self._queries = [{}]
self._centralized = [] self._centralized = []
self._calculate_get = [] self._calculate_get = []
@@ -170,45 +171,6 @@ class MiedaDevice(threading.Thread):
def register_update(self, update): def register_update(self, update):
self._updates.append(update) self._updates.append(update)
def connect(self, refresh=False):
try:
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._socket.settimeout(10)
MideaLogger.debug(f"Connecting to {self._ip_address}:{self._port}", self._device_id)
self._socket.connect((self._ip_address, self._port))
MideaLogger.debug(f"Connected", self._device_id)
if self._protocol == 3:
self._authenticate()
MideaLogger.debug(f"Authentication success", self._device_id)
self._device_connected(True)
if refresh:
self._refresh_status()
return True
except socket.timeout:
MideaLogger.debug(f"Connection timed out", self._device_id)
except socket.error:
MideaLogger.debug(f"Connection error", self._device_id)
except AuthException:
MideaLogger.debug(f"Authentication failed", self._device_id)
except ResponseException:
MideaLogger.debug(f"Unexpected response received", self._device_id)
except RefreshFailed:
MideaLogger.debug(f"Refresh status is timed out", self._device_id)
except Exception as e:
MideaLogger.error(f"Unknown error: {e.__traceback__.tb_frame.f_globals['__file__']}, "
f"{e.__traceback__.tb_lineno}, {repr(e)}")
if refresh:
self._device_connected(False)
self._socket = None
return False
def disconnect(self):
self._buffer = b""
if self._socket:
self._socket.close()
self._socket = None
@staticmethod @staticmethod
def _fetch_v2_message(msg): def _fetch_v2_message(msg):
result = [] result = []
@@ -329,7 +291,7 @@ class MiedaDevice(threading.Thread):
if not connected: if not connected:
MideaLogger.warning(f"Device {self._device_id} disconnected", self._device_id) MideaLogger.warning(f"Device {self._device_id} disconnected", self._device_id)
else: else:
MideaLogger.info(f"Device {self._device_id} connected", self._device_id) MideaLogger.debug(f"Device {self._device_id} connected", self._device_id)
self._update_all(status) self._update_all(status)
def _update_all(self, status): def _update_all(self, status):
@@ -337,64 +299,64 @@ class MiedaDevice(threading.Thread):
for update in self._updates: for update in self._updates:
update(status) update(status)
def open(self): # def open(self):
if not self._is_run: # if not self._is_run:
self._is_run = True # self._is_run = True
threading.Thread.start(self) # threading.Thread.start(self)
#
def close(self): # def close(self):
if self._is_run: # if self._is_run:
self._is_run = False # self._is_run = False
self._lua_runtime = None # self._lua_runtime = None
self.disconnect() # self.disconnect()
#
def run(self): # def run(self):
while self._is_run: # while self._is_run:
while self._socket is None: # while self._socket is None:
if self.connect(refresh=True) is False: # if self.connect(refresh=True) is False:
if not self._is_run: # if not self._is_run:
return # return
self.disconnect() # self.disconnect()
time.sleep(5) # time.sleep(5)
timeout_counter = 0 # timeout_counter = 0
start = time.time() # start = time.time()
previous_refresh = start # previous_refresh = start
previous_heartbeat = start # previous_heartbeat = start
self._socket.settimeout(1) # self._socket.settimeout(1)
while True: # while True:
try: # try:
now = time.time() # now = time.time()
if 0 < self._refresh_interval <= now - previous_refresh: # if 0 < self._refresh_interval <= now - previous_refresh:
self._refresh_status() # self._refresh_status()
previous_refresh = now # previous_refresh = now
if now - previous_heartbeat >= self._heartbeat_interval: # if now - previous_heartbeat >= self._heartbeat_interval:
self._send_heartbeat() # self._send_heartbeat()
previous_heartbeat = now # previous_heartbeat = now
msg = self._socket.recv(512) # msg = self._socket.recv(512)
msg_len = len(msg) # msg_len = len(msg)
if msg_len == 0: # if msg_len == 0:
raise socket.error("Connection closed by peer") # raise socket.error("Connection closed by peer")
result = self._parse_message(msg) # result = self._parse_message(msg)
if result == ParseMessageResult.ERROR: # if result == ParseMessageResult.ERROR:
MideaLogger.debug(f"Message 'ERROR' received") # MideaLogger.debug(f"Message 'ERROR' received")
self.disconnect() # self.disconnect()
break # break
elif result == ParseMessageResult.SUCCESS: # elif result == ParseMessageResult.SUCCESS:
timeout_counter = 0 # timeout_counter = 0
except socket.timeout: # except socket.timeout:
timeout_counter = timeout_counter + 1 # timeout_counter = timeout_counter + 1
if timeout_counter >= 120: # if timeout_counter >= 120:
MideaLogger.debug(f"Heartbeat timed out") # MideaLogger.debug(f"Heartbeat timed out")
self.disconnect() # self.disconnect()
break # break
except socket.error as e: # except socket.error as e:
MideaLogger.debug(f"Socket error {repr(e)}") # MideaLogger.debug(f"Socket error {repr(e)}")
self.disconnect() # self.disconnect()
break # break
except Exception as e: # except Exception as e:
MideaLogger.error(f"Unknown error :{e.__traceback__.tb_frame.f_globals['__file__']}, " # MideaLogger.error(f"Unknown error :{e.__traceback__.tb_frame.f_globals['__file__']}, "
f"{e.__traceback__.tb_lineno}, {repr(e)}") # f"{e.__traceback__.tb_lineno}, {repr(e)}")
self.disconnect() # self.disconnect()
break # break

View File

@@ -1,4 +1,4 @@
from homeassistant.const import * from homeassistant.const import Platform, UnitOfTemperature, PRECISION_HALVES
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
@@ -56,7 +56,7 @@ DEVICE_MAPPING = {
"aux_heat": "ptc", "aux_heat": "ptc",
"min_temp": 17, "min_temp": 17,
"max_temp": 30, "max_temp": 30,
"temperature_unit": TEMP_CELSIUS, "temperature_unit": UnitOfTemperature.CELSIUS,
"precision": PRECISION_HALVES, "precision": PRECISION_HALVES,
} }
}, },

View File

@@ -1,4 +1,4 @@
from homeassistant.const import * from homeassistant.const import Platform, UnitOfElectricPotential, UnitOfTemperature, UnitOfTime
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

View File

@@ -1,5 +1,6 @@
from homeassistant.const import * from homeassistant.const import Platform, UnitOfTemperature
from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass from homeassistant.components.sensor import SensorStateClass, SensorDeviceClass
from homeassistant.components.climate.const import PRECISION_HALVES
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
@@ -79,7 +80,7 @@ DEVICE_MAPPING = {
"aux_heat": "ptc", "aux_heat": "ptc",
"min_temp": 17, "min_temp": 17,
"max_temp": 30, "max_temp": 30,
"temperature_unit": TEMP_CELSIUS, "temperature_unit": UnitOfTemperature.CELSIUS,
"precision": PRECISION_HALVES, "precision": PRECISION_HALVES,
} }
}, },

View File

@@ -1,28 +1,30 @@
from homeassistant.components.fan import FanEntity, FanEntityFeature from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.const import ( from homeassistant.const import Platform
Platform, from .const import DOMAIN
CONF_DEVICE_ID,
CONF_DEVICE,
CONF_ENTITIES,
)
from .const import (
DOMAIN,
DEVICES
)
from .midea_entities import MideaEntity from .midea_entities import MideaEntity
from .core.logger import MideaLogger from . import load_device_config
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
device_id = config_entry.data.get(CONF_DEVICE_ID) account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
device = hass.data[DOMAIN][DEVICES][device_id].get(CONF_DEVICE) if not account_bucket:
manufacturer = hass.data[DOMAIN][DEVICES][device_id].get("manufacturer") async_add_entities([])
rationale = hass.data[DOMAIN][DEVICES][device_id].get("rationale") return
entities = hass.data[DOMAIN][DEVICES][device_id].get(CONF_ENTITIES).get(Platform.FAN) device_list = account_bucket.get("device_list", {})
coordinator_map = account_bucket.get("coordinator_map", {})
devs = [] devs = []
if entities is not None: for device_id, info in device_list.items():
for entity_key, config in entities.items(): device_type = info.get("type")
devs.append(MideaFanEntity(device, manufacturer, rationale, entity_key, config)) sn8 = info.get("sn8")
config = load_device_config(hass, device_type, sn8) or {}
entities_cfg = (config.get("entities") or {}).get(Platform.FAN, {})
manufacturer = config.get("manufacturer")
rationale = config.get("rationale")
coordinator = coordinator_map.get(device_id)
device = coordinator.device if coordinator else None
for entity_key, ecfg in entities_cfg.items():
devs.append(MideaFanEntity(device, manufacturer, rationale, entity_key, ecfg))
async_add_entities(devs) async_add_entities(devs)

View File

@@ -11,6 +11,7 @@ from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN from .const import DOMAIN
from .core.logger import MideaLogger
from .data_coordinator import MideaDataUpdateCoordinator from .data_coordinator import MideaDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -78,6 +79,7 @@ class MideaEntity(CoordinatorEntity[MideaDataUpdateCoordinator], Entity):
@property @property
def available(self) -> bool: def available(self) -> bool:
"""Return if entity is available.""" """Return if entity is available."""
MideaLogger.debug(f"available available={self.coordinator.data} ")
return self.coordinator.data.available return self.coordinator.data.available
async def _publish_command(self) -> None: async def _publish_command(self) -> None:

View File

@@ -1,27 +1,30 @@
from homeassistant.components.select import SelectEntity from homeassistant.components.select import SelectEntity
from homeassistant.const import ( from homeassistant.const import Platform
Platform, from .const import DOMAIN
CONF_DEVICE_ID,
CONF_DEVICE,
CONF_ENTITIES,
)
from .const import (
DOMAIN,
DEVICES
)
from .midea_entities import MideaEntity from .midea_entities import MideaEntity
from . import load_device_config
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
device_id = config_entry.data.get(CONF_DEVICE_ID) account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
device = hass.data[DOMAIN][DEVICES][device_id].get(CONF_DEVICE) if not account_bucket:
manufacturer = hass.data[DOMAIN][DEVICES][device_id].get("manufacturer") async_add_entities([])
rationale = hass.data[DOMAIN][DEVICES][device_id].get("rationale") return
entities = hass.data[DOMAIN][DEVICES][device_id].get(CONF_ENTITIES).get(Platform.SELECT) device_list = account_bucket.get("device_list", {})
coordinator_map = account_bucket.get("coordinator_map", {})
devs = [] devs = []
if entities is not None: for device_id, info in device_list.items():
for entity_key, config in entities.items(): device_type = info.get("type")
devs.append(MideaSelectEntity(device, manufacturer, rationale, entity_key, config)) sn8 = info.get("sn8")
config = load_device_config(hass, device_type, sn8) or {}
entities_cfg = (config.get("entities") or {}).get(Platform.SELECT, {})
manufacturer = config.get("manufacturer")
rationale = config.get("rationale")
coordinator = coordinator_map.get(device_id)
device = coordinator.device if coordinator else None
for entity_key, ecfg in entities_cfg.items():
devs.append(MideaSelectEntity(device, manufacturer, rationale, entity_key, ecfg))
async_add_entities(devs) async_add_entities(devs)

View File

@@ -1,18 +1,12 @@
from homeassistant.components.sensor import SensorEntity from homeassistant.components.sensor import SensorEntity
from homeassistant.const import ( from homeassistant.const import Platform
Platform,
CONF_DEVICE_ID,
CONF_ENTITIES, CONF_DEVICE
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ( from .const import DOMAIN
DOMAIN,
DEVICES
)
from .midea_entity import MideaEntity from .midea_entity import MideaEntity
from . import load_device_config
async def async_setup_entry( async def async_setup_entry(
@@ -21,19 +15,26 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up sensor entities for Midea devices.""" """Set up sensor entities for Midea devices."""
device_id = config_entry.data.get(CONF_DEVICE_ID) account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
device_data = hass.data[DOMAIN][DEVICES][device_id] if not account_bucket:
coordinator = device_data.get("coordinator") async_add_entities([])
device = device_data.get(CONF_DEVICE) return
manufacturer = device_data.get("manufacturer") device_list = account_bucket.get("device_list", {})
rationale = device_data.get("rationale") coordinator_map = account_bucket.get("coordinator_map", {})
entities = device_data.get(CONF_ENTITIES, {}).get(Platform.SENSOR, {})
devs = [] devs = []
if entities: for device_id, info in device_list.items():
for entity_key, config in entities.items(): device_type = info.get("type")
sn8 = info.get("sn8")
config = load_device_config(hass, device_type, sn8) or {}
entities_cfg = (config.get("entities") or {}).get(Platform.SENSOR, {})
manufacturer = config.get("manufacturer")
rationale = config.get("rationale")
coordinator = coordinator_map.get(device_id)
device = coordinator.device if coordinator else None
for entity_key, ecfg in entities_cfg.items():
devs.append(MideaSensorEntity( devs.append(MideaSensorEntity(
coordinator, device, manufacturer, rationale, entity_key, config coordinator, device, manufacturer, rationale, entity_key, ecfg
)) ))
async_add_entities(devs) async_add_entities(devs)

View File

@@ -1,18 +1,13 @@
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import SwitchEntity
from homeassistant.const import ( from homeassistant.const import Platform
Platform,
CONF_DEVICE_ID,
CONF_ENTITIES, CONF_DEVICE,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ( from .const import DOMAIN
DOMAIN, from .core.logger import MideaLogger
DEVICES
)
from .midea_entity import MideaEntity from .midea_entity import MideaEntity
from . import load_device_config
async def async_setup_entry( async def async_setup_entry(
@@ -21,19 +16,26 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up switch entities for Midea devices.""" """Set up switch entities for Midea devices."""
device_id = config_entry.data.get(CONF_DEVICE_ID) account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
device_data = hass.data[DOMAIN][DEVICES][device_id] if not account_bucket:
coordinator = device_data.get("coordinator") async_add_entities([])
device = device_data.get(CONF_DEVICE) return
manufacturer = device_data.get("manufacturer") device_list = account_bucket.get("device_list", {})
rationale = device_data.get("rationale") coordinator_map = account_bucket.get("coordinator_map", {})
entities = device_data.get(CONF_ENTITIES, {}).get(Platform.SWITCH, {})
devs = [] devs = []
if entities: for device_id, info in device_list.items():
for entity_key, config in entities.items(): device_type = info.get("type")
sn8 = info.get("sn8")
config = load_device_config(hass, device_type, sn8) or {}
entities_cfg = (config.get("entities") or {}).get(Platform.SWITCH, {})
manufacturer = config.get("manufacturer")
rationale = config.get("rationale")
coordinator = coordinator_map.get(device_id)
device = coordinator.device if coordinator else None
for entity_key, ecfg in entities_cfg.items():
devs.append(MideaSwitchEntity( devs.append(MideaSwitchEntity(
coordinator, device, manufacturer, rationale, entity_key, config coordinator, device, manufacturer, rationale, entity_key, ecfg
)) ))
async_add_entities(devs) async_add_entities(devs)

View File

@@ -1,28 +1,33 @@
from homeassistant.components.water_heater import WaterHeaterEntity, WaterHeaterEntityFeature from homeassistant.components.water_heater import WaterHeaterEntity, WaterHeaterEntityFeature
from homeassistant.const import ( from homeassistant.const import (
Platform, Platform,
CONF_DEVICE_ID,
CONF_DEVICE,
CONF_ENTITIES,
ATTR_TEMPERATURE ATTR_TEMPERATURE
) )
from .const import ( from .const import DOMAIN
DOMAIN,
DEVICES
)
from .midea_entities import MideaEntity from .midea_entities import MideaEntity
from . import load_device_config
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
device_id = config_entry.data.get(CONF_DEVICE_ID) account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
device = hass.data[DOMAIN][DEVICES][device_id].get(CONF_DEVICE) if not account_bucket:
manufacturer = hass.data[DOMAIN][DEVICES][device_id].get("manufacturer") async_add_entities([])
rationale = hass.data[DOMAIN][DEVICES][device_id].get("rationale") return
entities = hass.data[DOMAIN][DEVICES][device_id].get(CONF_ENTITIES).get(Platform.WATER_HEATER) device_list = account_bucket.get("device_list", {})
coordinator_map = account_bucket.get("coordinator_map", {})
devs = [] devs = []
if entities is not None: for device_id, info in device_list.items():
for entity_key, config in entities.items(): device_type = info.get("type")
devs.append(MideaWaterHeaterEntityEntity(device, manufacturer, rationale, entity_key, config)) sn8 = info.get("sn8")
config = load_device_config(hass, device_type, sn8) or {}
entities_cfg = (config.get("entities") or {}).get(Platform.WATER_HEATER, {})
manufacturer = config.get("manufacturer")
rationale = config.get("rationale")
coordinator = coordinator_map.get(device_id)
device = coordinator.device if coordinator else None
for entity_key, ecfg in entities_cfg.items():
devs.append(MideaWaterHeaterEntityEntity(device, manufacturer, rationale, entity_key, ecfg))
async_add_entities(devs) async_add_entities(devs)