mirror of
https://github.com/sususweet/midea-meiju-codec.git
synced 2025-09-28 02:32:40 +00:00
Add online status
This commit is contained in:
@@ -9,6 +9,7 @@ try:
|
||||
except ImportError:
|
||||
from homeassistant.util.json import save_json
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.core import (
|
||||
HomeAssistant,
|
||||
ServiceCall
|
||||
@@ -26,9 +27,11 @@ from homeassistant.const import (
|
||||
CONF_DEVICE,
|
||||
CONF_ENTITIES
|
||||
)
|
||||
|
||||
from .core.logger import MideaLogger
|
||||
from .core.device import MiedaDevice
|
||||
from .data_coordinator import MideaDataUpdateCoordinator
|
||||
from .core.cloud import get_midea_cloud
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
DEVICES,
|
||||
@@ -39,20 +42,21 @@ from .const import (
|
||||
CONF_SN8,
|
||||
CONF_SN,
|
||||
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.SENSOR,
|
||||
Platform.SWITCH,
|
||||
# Platform.SENSOR,
|
||||
# Platform.SWITCH,
|
||||
Platform.CLIMATE,
|
||||
Platform.SELECT,
|
||||
Platform.WATER_HEATER,
|
||||
Platform.FAN
|
||||
]
|
||||
|
||||
|
||||
def get_sn8_used(hass: HomeAssistant, sn8):
|
||||
entries = hass.config_entries.async_entries(DOMAIN)
|
||||
count = 0
|
||||
@@ -175,83 +179,87 @@ async def async_setup(hass: HomeAssistant, config: ConfigType):
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
|
||||
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:
|
||||
return True
|
||||
name = config_entry.data.get(CONF_NAME)
|
||||
device_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||
device_type = config_entry.data.get(CONF_TYPE)
|
||||
token = config_entry.data.get(CONF_TOKEN)
|
||||
key = config_entry.data.get(CONF_KEY)
|
||||
ip_address = config_entry.options.get(CONF_IP_ADDRESS, None)
|
||||
if not ip_address:
|
||||
ip_address = config_entry.data.get(CONF_IP_ADDRESS)
|
||||
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,
|
||||
account = config_entry.data.get(CONF_ACCOUNT)
|
||||
password = config_entry.data.get(CONF_PASSWORD_KEY)
|
||||
server = config_entry.data.get(CONF_SERVER_KEY)
|
||||
cloud_name = CONF_SERVERS.get(server)
|
||||
cloud = get_midea_cloud(
|
||||
cloud_name=cloud_name,
|
||||
session=async_get_clientsession(hass),
|
||||
account=account,
|
||||
password=password,
|
||||
)
|
||||
if refresh_interval is not None:
|
||||
device.set_refresh_interval(refresh_interval)
|
||||
device.open()
|
||||
if DOMAIN not in hass.data:
|
||||
hass.data[DOMAIN] = {}
|
||||
if DEVICES not in hass.data[DOMAIN]:
|
||||
hass.data[DOMAIN][DEVICES] = {}
|
||||
if not cloud or not await cloud.login():
|
||||
MideaLogger.error("Midea cloud login failed")
|
||||
return False
|
||||
|
||||
# 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)
|
||||
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][DEVICES][device_id][CONF_DEVICE] = device
|
||||
hass.data[DOMAIN][DEVICES][device_id]["coordinator"] = coordinator
|
||||
hass.data[DOMAIN][DEVICES][device_id][CONF_ENTITIES] = {}
|
||||
hass.data[DOMAIN]["accounts"][config_entry.entry_id] = bucket
|
||||
|
||||
config = load_device_config(hass, device_type, sn8)
|
||||
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)
|
||||
hass.async_create_task(hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS))
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry):
|
||||
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:
|
||||
device: MiedaDevice = hass.data[DOMAIN][DEVICES][device_id][CONF_DEVICE]
|
||||
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")
|
||||
os.remove(lua_file)
|
||||
remove_device_config(hass, device.sn8)
|
||||
device.close()
|
||||
# device.close()
|
||||
hass.data[DOMAIN][DEVICES].pop(device_id)
|
||||
for platform in ALL_PLATFORM:
|
||||
await hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
||||
|
@@ -2,20 +2,14 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntity,
|
||||
BinarySensorDeviceClass
|
||||
)
|
||||
from homeassistant.const import (
|
||||
Platform,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_ENTITIES, CONF_DEVICE
|
||||
)
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
DEVICES
|
||||
)
|
||||
from .const import DOMAIN
|
||||
from .midea_entity import MideaEntity
|
||||
from . import load_device_config
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -24,19 +18,29 @@ async def async_setup_entry(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up binary sensor entities for Midea devices."""
|
||||
device_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||
device_data = hass.data[DOMAIN][DEVICES][device_id]
|
||||
coordinator = device_data.get("coordinator")
|
||||
device = device_data.get(CONF_DEVICE)
|
||||
manufacturer = device_data.get("manufacturer")
|
||||
rationale = device_data.get("rationale")
|
||||
entities = device_data.get(CONF_ENTITIES, {}).get(Platform.BINARY_SENSOR, {})
|
||||
account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
|
||||
if not account_bucket:
|
||||
async_add_entities([])
|
||||
return
|
||||
device_list = account_bucket.get("device_list", {})
|
||||
coordinator_map = account_bucket.get("coordinator_map", {})
|
||||
|
||||
devs = [MideaDeviceStatusSensorEntity(coordinator, device, manufacturer, rationale, "Status", {})]
|
||||
if entities:
|
||||
for entity_key, config in entities.items():
|
||||
devs = []
|
||||
for device_id, info in device_list.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(
|
||||
coordinator, device, manufacturer, rationale, entity_key, config
|
||||
coordinator, device, manufacturer, rationale, entity_key, ecfg
|
||||
))
|
||||
async_add_entities(devs)
|
||||
|
||||
|
@@ -6,20 +6,16 @@ from homeassistant.components.climate import (
|
||||
)
|
||||
from homeassistant.const import (
|
||||
Platform,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_ENTITIES,
|
||||
ATTR_TEMPERATURE, CONF_DEVICE
|
||||
ATTR_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
DEVICES
|
||||
)
|
||||
from .const import DOMAIN
|
||||
from .midea_entity import MideaEntity
|
||||
from .midea_entities import Rationale
|
||||
from . import load_device_config
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -28,19 +24,27 @@ async def async_setup_entry(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up climate entities for Midea devices."""
|
||||
device_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||
device_data = hass.data[DOMAIN][DEVICES][device_id]
|
||||
coordinator = device_data.get("coordinator")
|
||||
device = device_data.get(CONF_DEVICE)
|
||||
manufacturer = device_data.get("manufacturer")
|
||||
rationale = device_data.get("rationale")
|
||||
entities = device_data.get(CONF_ENTITIES, {}).get(Platform.CLIMATE, {})
|
||||
# 账号型 entry:从 __init__ 写入的 accounts 桶加载设备和协调器
|
||||
account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
|
||||
if not account_bucket:
|
||||
async_add_entities([])
|
||||
return
|
||||
device_list = account_bucket.get("device_list", {})
|
||||
coordinator_map = account_bucket.get("coordinator_map", {})
|
||||
|
||||
devs = []
|
||||
if entities:
|
||||
for entity_key, config in entities.items():
|
||||
for device_id, info in device_list.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(
|
||||
coordinator, device, manufacturer, rationale, entity_key, config
|
||||
coordinator, device, manufacturer, rationale, entity_key, ecfg
|
||||
))
|
||||
async_add_entities(devs)
|
||||
|
||||
@@ -81,8 +85,8 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
|
||||
features |= ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
if self._key_preset_modes is not None:
|
||||
features |= ClimateEntityFeature.PRESET_MODE
|
||||
if self._key_aux_heat is not None:
|
||||
features |= ClimateEntityFeature.AUX_HEAT
|
||||
# if self._key_aux_heat is not None:
|
||||
# features |= ClimateEntityFeature.AUX_HEAT
|
||||
if self._key_swing_modes is not None:
|
||||
features |= ClimateEntityFeature.SWING_MODE
|
||||
if self._key_fan_modes is not None:
|
||||
@@ -227,7 +231,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
|
||||
return
|
||||
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."""
|
||||
if dict_config is None:
|
||||
return None
|
||||
@@ -238,7 +242,7 @@ class MideaClimateEntity(MideaEntity, ClimateEntity):
|
||||
match = True
|
||||
for attr_key, attr_value in config.items():
|
||||
device_value = self.device_attributes.get(attr_key)
|
||||
if rationale == Rationale.EQUAL:
|
||||
if rationale == Rationale.EQUALLY:
|
||||
if device_value != attr_value:
|
||||
match = False
|
||||
break
|
||||
|
@@ -1,293 +1,68 @@
|
||||
import voluptuous as vol
|
||||
import logging
|
||||
import os
|
||||
import ipaddress
|
||||
from typing import Any
|
||||
from homeassistant.helpers.aiohttp_client import async_create_clientsession
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.config_entries import ConfigFlowResult
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.const import (
|
||||
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 (
|
||||
DOMAIN,
|
||||
CONF_REFRESH_INTERVAL,
|
||||
STORAGE_PATH,
|
||||
CONF_ACCOUNT,
|
||||
CONF_SERVER,
|
||||
CONF_HOME,
|
||||
CONF_KEY,
|
||||
CONF_SN8,
|
||||
CONF_SN,
|
||||
CONF_MODEL_NUMBER,
|
||||
CONF_LUA_FILE
|
||||
CONF_PASSWORD,
|
||||
DOMAIN,
|
||||
CONF_SERVER, CONF_SERVERS
|
||||
)
|
||||
from .core.cloud import get_midea_cloud
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
servers = {
|
||||
1: "MSmartHome",
|
||||
2: "美的美居",
|
||||
}
|
||||
|
||||
|
||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
_session = None
|
||||
_cloud = None
|
||||
_current_home = None
|
||||
_device_list = {}
|
||||
_device = None
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
return OptionsFlowHandler(config_entry)
|
||||
|
||||
def _get_configured_account(self):
|
||||
for entry in self._async_current_entries():
|
||||
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):
|
||||
async def async_step_user(self, user_input: dict[str, Any] | None = None) -> ConfigFlowResult:
|
||||
errors: dict[str, str] = {}
|
||||
if self._session is None:
|
||||
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 self._cloud is None:
|
||||
self._cloud = get_midea_cloud(
|
||||
cloud = get_midea_cloud(
|
||||
session=self._session,
|
||||
cloud_name=servers[user_input[CONF_SERVER]],
|
||||
cloud_name=CONF_SERVERS[user_input[CONF_SERVER]],
|
||||
account=user_input[CONF_ACCOUNT],
|
||||
password=user_input[CONF_PASSWORD]
|
||||
)
|
||||
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(
|
||||
title=f"{user_input[CONF_ACCOUNT]}",
|
||||
title=user_input[CONF_ACCOUNT],
|
||||
data={
|
||||
CONF_TYPE: CONF_ACCOUNT,
|
||||
CONF_ACCOUNT: user_input[CONF_ACCOUNT],
|
||||
CONF_PASSWORD: user_input[CONF_PASSWORD],
|
||||
CONF_SERVER: user_input[CONF_SERVER]
|
||||
})
|
||||
},
|
||||
)
|
||||
else:
|
||||
self._cloud = None
|
||||
return await self.async_step_user(error="login_failed")
|
||||
errors["base"] = "login_failed"
|
||||
except Exception as e:
|
||||
_LOGGER.error(f"Login error: {e}")
|
||||
self._cloud = None
|
||||
return await self.async_step_user(error="login_failed")
|
||||
_LOGGER.exception("Login error: %s", e)
|
||||
errors["base"] = "login_failed"
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema({
|
||||
vol.Required(CONF_ACCOUNT): 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
|
||||
)
|
||||
|
||||
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
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
|
||||
@@ -296,64 +71,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
self._config_entry = config_entry
|
||||
|
||||
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")
|
||||
if user_input is not None:
|
||||
if user_input.get("option") == 1:
|
||||
return await self.async_step_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
|
||||
)
|
||||
# 不再提供任何可配置项
|
||||
return self.async_abort(reason="account_unsupport_config")
|
||||
# 不提供 reset/configure 等选项步骤
|
File diff suppressed because one or more lines are too long
@@ -96,9 +96,10 @@ class MideaCloud:
|
||||
break
|
||||
except Exception as e:
|
||||
pass
|
||||
print(response)
|
||||
if int(response["code"]) == 0 and "data" in response:
|
||||
return response["data"]
|
||||
print(response)
|
||||
|
||||
return None
|
||||
|
||||
async def _get_login_id(self) -> str | None:
|
||||
|
@@ -32,13 +32,14 @@ class MiedaDevice(threading.Thread):
|
||||
name: str,
|
||||
device_id: int,
|
||||
device_type: int,
|
||||
ip_address: str,
|
||||
port: int,
|
||||
ip_address: str | None,
|
||||
port: int | None,
|
||||
token: str | None,
|
||||
key: str | None,
|
||||
protocol: int,
|
||||
model: str | None,
|
||||
subtype: int | None,
|
||||
connected: bool,
|
||||
sn: str | None,
|
||||
sn8: str | None,
|
||||
lua_file: str | None):
|
||||
@@ -68,7 +69,7 @@ class MiedaDevice(threading.Thread):
|
||||
}
|
||||
self._refresh_interval = 30
|
||||
self._heartbeat_interval = 10
|
||||
self._connected = False
|
||||
self._device_connected(connected)
|
||||
self._queries = [{}]
|
||||
self._centralized = []
|
||||
self._calculate_get = []
|
||||
@@ -170,45 +171,6 @@ class MiedaDevice(threading.Thread):
|
||||
def register_update(self, 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
|
||||
def _fetch_v2_message(msg):
|
||||
result = []
|
||||
@@ -329,7 +291,7 @@ class MiedaDevice(threading.Thread):
|
||||
if not connected:
|
||||
MideaLogger.warning(f"Device {self._device_id} disconnected", self._device_id)
|
||||
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)
|
||||
|
||||
def _update_all(self, status):
|
||||
@@ -337,64 +299,64 @@ class MiedaDevice(threading.Thread):
|
||||
for update in self._updates:
|
||||
update(status)
|
||||
|
||||
def open(self):
|
||||
if not self._is_run:
|
||||
self._is_run = True
|
||||
threading.Thread.start(self)
|
||||
|
||||
def close(self):
|
||||
if self._is_run:
|
||||
self._is_run = False
|
||||
self._lua_runtime = None
|
||||
self.disconnect()
|
||||
|
||||
def run(self):
|
||||
while self._is_run:
|
||||
while self._socket is None:
|
||||
if self.connect(refresh=True) is False:
|
||||
if not self._is_run:
|
||||
return
|
||||
self.disconnect()
|
||||
time.sleep(5)
|
||||
timeout_counter = 0
|
||||
start = time.time()
|
||||
previous_refresh = start
|
||||
previous_heartbeat = start
|
||||
self._socket.settimeout(1)
|
||||
while True:
|
||||
try:
|
||||
now = time.time()
|
||||
if 0 < self._refresh_interval <= now - previous_refresh:
|
||||
self._refresh_status()
|
||||
previous_refresh = now
|
||||
if now - previous_heartbeat >= self._heartbeat_interval:
|
||||
self._send_heartbeat()
|
||||
previous_heartbeat = now
|
||||
msg = self._socket.recv(512)
|
||||
msg_len = len(msg)
|
||||
if msg_len == 0:
|
||||
raise socket.error("Connection closed by peer")
|
||||
result = self._parse_message(msg)
|
||||
if result == ParseMessageResult.ERROR:
|
||||
MideaLogger.debug(f"Message 'ERROR' received")
|
||||
self.disconnect()
|
||||
break
|
||||
elif result == ParseMessageResult.SUCCESS:
|
||||
timeout_counter = 0
|
||||
except socket.timeout:
|
||||
timeout_counter = timeout_counter + 1
|
||||
if timeout_counter >= 120:
|
||||
MideaLogger.debug(f"Heartbeat timed out")
|
||||
self.disconnect()
|
||||
break
|
||||
except socket.error as e:
|
||||
MideaLogger.debug(f"Socket error {repr(e)}")
|
||||
self.disconnect()
|
||||
break
|
||||
except Exception as e:
|
||||
MideaLogger.error(f"Unknown error :{e.__traceback__.tb_frame.f_globals['__file__']}, "
|
||||
f"{e.__traceback__.tb_lineno}, {repr(e)}")
|
||||
self.disconnect()
|
||||
break
|
||||
# def open(self):
|
||||
# if not self._is_run:
|
||||
# self._is_run = True
|
||||
# threading.Thread.start(self)
|
||||
#
|
||||
# def close(self):
|
||||
# if self._is_run:
|
||||
# self._is_run = False
|
||||
# self._lua_runtime = None
|
||||
# self.disconnect()
|
||||
#
|
||||
# def run(self):
|
||||
# while self._is_run:
|
||||
# while self._socket is None:
|
||||
# if self.connect(refresh=True) is False:
|
||||
# if not self._is_run:
|
||||
# return
|
||||
# self.disconnect()
|
||||
# time.sleep(5)
|
||||
# timeout_counter = 0
|
||||
# start = time.time()
|
||||
# previous_refresh = start
|
||||
# previous_heartbeat = start
|
||||
# self._socket.settimeout(1)
|
||||
# while True:
|
||||
# try:
|
||||
# now = time.time()
|
||||
# if 0 < self._refresh_interval <= now - previous_refresh:
|
||||
# self._refresh_status()
|
||||
# previous_refresh = now
|
||||
# if now - previous_heartbeat >= self._heartbeat_interval:
|
||||
# self._send_heartbeat()
|
||||
# previous_heartbeat = now
|
||||
# msg = self._socket.recv(512)
|
||||
# msg_len = len(msg)
|
||||
# if msg_len == 0:
|
||||
# raise socket.error("Connection closed by peer")
|
||||
# result = self._parse_message(msg)
|
||||
# if result == ParseMessageResult.ERROR:
|
||||
# MideaLogger.debug(f"Message 'ERROR' received")
|
||||
# self.disconnect()
|
||||
# break
|
||||
# elif result == ParseMessageResult.SUCCESS:
|
||||
# timeout_counter = 0
|
||||
# except socket.timeout:
|
||||
# timeout_counter = timeout_counter + 1
|
||||
# if timeout_counter >= 120:
|
||||
# MideaLogger.debug(f"Heartbeat timed out")
|
||||
# self.disconnect()
|
||||
# break
|
||||
# except socket.error as e:
|
||||
# MideaLogger.debug(f"Socket error {repr(e)}")
|
||||
# self.disconnect()
|
||||
# break
|
||||
# except Exception as e:
|
||||
# MideaLogger.error(f"Unknown error :{e.__traceback__.tb_frame.f_globals['__file__']}, "
|
||||
# f"{e.__traceback__.tb_lineno}, {repr(e)}")
|
||||
# self.disconnect()
|
||||
# break
|
||||
|
||||
|
||||
|
@@ -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.binary_sensor import BinarySensorDeviceClass
|
||||
from homeassistant.components.switch import SwitchDeviceClass
|
||||
@@ -56,7 +56,7 @@ DEVICE_MAPPING = {
|
||||
"aux_heat": "ptc",
|
||||
"min_temp": 17,
|
||||
"max_temp": 30,
|
||||
"temperature_unit": TEMP_CELSIUS,
|
||||
"temperature_unit": UnitOfTemperature.CELSIUS,
|
||||
"precision": PRECISION_HALVES,
|
||||
}
|
||||
},
|
||||
|
@@ -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.binary_sensor import BinarySensorDeviceClass
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
from homeassistant.const import *
|
||||
from homeassistant.const import Platform, UnitOfTemperature
|
||||
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.switch import SwitchDeviceClass
|
||||
|
||||
@@ -79,7 +80,7 @@ DEVICE_MAPPING = {
|
||||
"aux_heat": "ptc",
|
||||
"min_temp": 17,
|
||||
"max_temp": 30,
|
||||
"temperature_unit": TEMP_CELSIUS,
|
||||
"temperature_unit": UnitOfTemperature.CELSIUS,
|
||||
"precision": PRECISION_HALVES,
|
||||
}
|
||||
},
|
||||
|
@@ -1,28 +1,30 @@
|
||||
from homeassistant.components.fan import FanEntity, FanEntityFeature
|
||||
from homeassistant.const import (
|
||||
Platform,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_DEVICE,
|
||||
CONF_ENTITIES,
|
||||
)
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
DEVICES
|
||||
)
|
||||
from homeassistant.const import Platform
|
||||
from .const import DOMAIN
|
||||
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):
|
||||
device_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||
device = hass.data[DOMAIN][DEVICES][device_id].get(CONF_DEVICE)
|
||||
manufacturer = hass.data[DOMAIN][DEVICES][device_id].get("manufacturer")
|
||||
rationale = hass.data[DOMAIN][DEVICES][device_id].get("rationale")
|
||||
entities = hass.data[DOMAIN][DEVICES][device_id].get(CONF_ENTITIES).get(Platform.FAN)
|
||||
account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
|
||||
if not account_bucket:
|
||||
async_add_entities([])
|
||||
return
|
||||
device_list = account_bucket.get("device_list", {})
|
||||
coordinator_map = account_bucket.get("coordinator_map", {})
|
||||
|
||||
devs = []
|
||||
if entities is not None:
|
||||
for entity_key, config in entities.items():
|
||||
devs.append(MideaFanEntity(device, manufacturer, rationale, entity_key, config))
|
||||
for device_id, info in device_list.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.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)
|
||||
|
||||
|
||||
|
@@ -11,6 +11,7 @@ from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .core.logger import MideaLogger
|
||||
from .data_coordinator import MideaDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -78,6 +79,7 @@ class MideaEntity(CoordinatorEntity[MideaDataUpdateCoordinator], Entity):
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if entity is available."""
|
||||
MideaLogger.debug(f"available available={self.coordinator.data} ")
|
||||
return self.coordinator.data.available
|
||||
|
||||
async def _publish_command(self) -> None:
|
||||
|
@@ -1,27 +1,30 @@
|
||||
from homeassistant.components.select import SelectEntity
|
||||
from homeassistant.const import (
|
||||
Platform,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_DEVICE,
|
||||
CONF_ENTITIES,
|
||||
)
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
DEVICES
|
||||
)
|
||||
from homeassistant.const import Platform
|
||||
from .const import DOMAIN
|
||||
from .midea_entities import MideaEntity
|
||||
from . import load_device_config
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
device_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||
device = hass.data[DOMAIN][DEVICES][device_id].get(CONF_DEVICE)
|
||||
manufacturer = hass.data[DOMAIN][DEVICES][device_id].get("manufacturer")
|
||||
rationale = hass.data[DOMAIN][DEVICES][device_id].get("rationale")
|
||||
entities = hass.data[DOMAIN][DEVICES][device_id].get(CONF_ENTITIES).get(Platform.SELECT)
|
||||
account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
|
||||
if not account_bucket:
|
||||
async_add_entities([])
|
||||
return
|
||||
device_list = account_bucket.get("device_list", {})
|
||||
coordinator_map = account_bucket.get("coordinator_map", {})
|
||||
|
||||
devs = []
|
||||
if entities is not None:
|
||||
for entity_key, config in entities.items():
|
||||
devs.append(MideaSelectEntity(device, manufacturer, rationale, entity_key, config))
|
||||
for device_id, info in device_list.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.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)
|
||||
|
||||
|
||||
|
@@ -1,18 +1,12 @@
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.const import (
|
||||
Platform,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_ENTITIES, CONF_DEVICE
|
||||
)
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
DEVICES
|
||||
)
|
||||
from .const import DOMAIN
|
||||
from .midea_entity import MideaEntity
|
||||
from . import load_device_config
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -21,19 +15,26 @@ async def async_setup_entry(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up sensor entities for Midea devices."""
|
||||
device_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||
device_data = hass.data[DOMAIN][DEVICES][device_id]
|
||||
coordinator = device_data.get("coordinator")
|
||||
device = device_data.get(CONF_DEVICE)
|
||||
manufacturer = device_data.get("manufacturer")
|
||||
rationale = device_data.get("rationale")
|
||||
entities = device_data.get(CONF_ENTITIES, {}).get(Platform.SENSOR, {})
|
||||
account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
|
||||
if not account_bucket:
|
||||
async_add_entities([])
|
||||
return
|
||||
device_list = account_bucket.get("device_list", {})
|
||||
coordinator_map = account_bucket.get("coordinator_map", {})
|
||||
|
||||
devs = []
|
||||
if entities:
|
||||
for entity_key, config in entities.items():
|
||||
for device_id, info in device_list.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(
|
||||
coordinator, device, manufacturer, rationale, entity_key, config
|
||||
coordinator, device, manufacturer, rationale, entity_key, ecfg
|
||||
))
|
||||
async_add_entities(devs)
|
||||
|
||||
|
@@ -1,18 +1,13 @@
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.const import (
|
||||
Platform,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_ENTITIES, CONF_DEVICE,
|
||||
)
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
DEVICES
|
||||
)
|
||||
from .const import DOMAIN
|
||||
from .core.logger import MideaLogger
|
||||
from .midea_entity import MideaEntity
|
||||
from . import load_device_config
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -21,19 +16,26 @@ async def async_setup_entry(
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up switch entities for Midea devices."""
|
||||
device_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||
device_data = hass.data[DOMAIN][DEVICES][device_id]
|
||||
coordinator = device_data.get("coordinator")
|
||||
device = device_data.get(CONF_DEVICE)
|
||||
manufacturer = device_data.get("manufacturer")
|
||||
rationale = device_data.get("rationale")
|
||||
entities = device_data.get(CONF_ENTITIES, {}).get(Platform.SWITCH, {})
|
||||
account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
|
||||
if not account_bucket:
|
||||
async_add_entities([])
|
||||
return
|
||||
device_list = account_bucket.get("device_list", {})
|
||||
coordinator_map = account_bucket.get("coordinator_map", {})
|
||||
|
||||
devs = []
|
||||
if entities:
|
||||
for entity_key, config in entities.items():
|
||||
for device_id, info in device_list.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(
|
||||
coordinator, device, manufacturer, rationale, entity_key, config
|
||||
coordinator, device, manufacturer, rationale, entity_key, ecfg
|
||||
))
|
||||
async_add_entities(devs)
|
||||
|
||||
|
@@ -1,28 +1,33 @@
|
||||
from homeassistant.components.water_heater import WaterHeaterEntity, WaterHeaterEntityFeature
|
||||
from homeassistant.const import (
|
||||
Platform,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_DEVICE,
|
||||
CONF_ENTITIES,
|
||||
ATTR_TEMPERATURE
|
||||
)
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
DEVICES
|
||||
)
|
||||
from .const import DOMAIN
|
||||
from .midea_entities import MideaEntity
|
||||
from . import load_device_config
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
device_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||
device = hass.data[DOMAIN][DEVICES][device_id].get(CONF_DEVICE)
|
||||
manufacturer = hass.data[DOMAIN][DEVICES][device_id].get("manufacturer")
|
||||
rationale = hass.data[DOMAIN][DEVICES][device_id].get("rationale")
|
||||
entities = hass.data[DOMAIN][DEVICES][device_id].get(CONF_ENTITIES).get(Platform.WATER_HEATER)
|
||||
account_bucket = hass.data.get(DOMAIN, {}).get("accounts", {}).get(config_entry.entry_id)
|
||||
if not account_bucket:
|
||||
async_add_entities([])
|
||||
return
|
||||
device_list = account_bucket.get("device_list", {})
|
||||
coordinator_map = account_bucket.get("coordinator_map", {})
|
||||
|
||||
devs = []
|
||||
if entities is not None:
|
||||
for entity_key, config in entities.items():
|
||||
devs.append(MideaWaterHeaterEntityEntity(device, manufacturer, rationale, entity_key, config))
|
||||
for device_id, info in device_list.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.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)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user