优化设备添加流程

This commit is contained in:
unknown
2023-09-03 22:15:41 +08:00
parent fc82b6de79
commit faae480fd8
7 changed files with 175 additions and 99 deletions

View File

@@ -56,6 +56,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry):
port = config_entry.data.get(CONF_PORT)
model = config_entry.data.get(CONF_MODEL)
protocol = config_entry.data.get(CONF_PROTOCOL)
sn = config_entry.data.get("sn")
sn8 = config_entry.data.get("sn8")
lua_file = config_entry.data.get("lua_file")
_LOGGER.error(f"lua_file = {lua_file}")
if protocol == 3 and (key is None or key is None):
@@ -71,6 +73,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry):
key=key,
protocol=protocol,
model=model,
sn=sn,
sn8=sn8,
lua_file=lua_file,
)
if device:

View File

@@ -18,7 +18,7 @@ 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].get(device_id)
binary_sensors = []
sensor = MideaDeviceStatusSensor(device, "online")
sensor = MideaDeviceStatusSensor(device, "status")
binary_sensors.append(sensor)
async_add_entities(binary_sensors)
@@ -32,6 +32,14 @@ class MideaDeviceStatusSensor(MideaEntity):
def state(self):
return STATE_ON if self._device.connected else STATE_OFF
@property
def name(self):
return f"{self._device_name} Status"
@property
def icon(self):
return "mdi:devices"
@property
def is_on(self):
return self.state == STATE_ON
@@ -46,8 +54,6 @@ class MideaDeviceStatusSensor(MideaEntity):
def update_state(self, status):
try:
_LOGGER.debug("=" * 50)
self.schedule_update_ha_state()
_LOGGER.debug("-" * 50)
except Exception as e:
pass

View File

@@ -1,6 +1,7 @@
import voluptuous as vol
import logging
import os
import ipaddress
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant import config_entries
from homeassistant.const import (
@@ -15,7 +16,6 @@ from homeassistant.const import (
CONF_PROTOCOL,
CONF_TOKEN,
CONF_NAME
)
from .core.cloud import MeijuCloudExtend
from .core.discover import discover
@@ -36,6 +36,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
_cloud = None
_current_home = None
_device_list = {}
_device = None
def _get_configured_account(self):
for entry in self._async_current_entries():
@@ -49,16 +50,29 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
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):
username, password = self._get_configured_account()
if username is not None and password is not None:
if self._session is None:
self._session = async_create_clientsession(self.hass)
username, password = self._get_configured_account()
if username is not None and password is not None:
if self._cloud is None:
self._cloud = MeijuCloudExtend(self._session, username, password)
if await self._cloud.login():
return await self.async_step_home()
else:
return await self.async_step_user(error="account_invalid")
if user_input is not None:
if self._cloud is None:
self._cloud = MeijuCloudExtend(self._session, user_input[CONF_USERNAME], user_input[CONF_PASSWORD])
if await self._cloud.login():
return self.async_create_entry(
title=f"{user_input[CONF_USERNAME]}",
data={
@@ -66,6 +80,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
CONF_USERNAME: user_input[CONF_USERNAME],
CONF_PASSWORD: user_input[CONF_PASSWORD]
})
else:
self._cloud = None
return await self.async_step_user(error="login_failed")
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({
@@ -96,86 +113,17 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if user_input is not None:
# 下载lua
# 本地尝试连接设备
device = self._device_list[user_input[CONF_DEVICE]]
if not device.get("online"):
self._device = self._device_list[user_input[CONF_DEVICE]]
if not self._device.get("online"):
return await self.async_step_device(error="offline_error")
discover_devices = discover([device["type"]])
_LOGGER.debug(discover_devices)
if discover_devices is None or len(discover_devices) == 0:
return await self.async_step_device(error="discover_failed")
current_device = discover_devices.get(user_input[CONF_DEVICE])
if current_device is None:
return await self.async_step_device(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.get_lua(device["sn"], device["type"], path, device["enterprise_code"])
if file is None:
return await self.async_step_device(error="download_lua_failed")
use_token = None
use_key = None
connected = False
if current_device.get("protocol") == 3:
for byte_order_big in [False, True]:
token, key = await self._cloud.get_token(user_input[CONF_DEVICE], byte_order_big=byte_order_big)
if token and key:
dm = MiedaDevice(
name=device.get("name"),
device_id=user_input[CONF_DEVICE],
device_type=current_device.get(CONF_TYPE),
ip_address=current_device.get(CONF_IP_ADDRESS),
port=current_device.get(CONF_PORT),
token=token,
key=key,
protocol=3,
model=device.get(CONF_MODEL),
lua_file=None
)
if dm.connect():
use_token = token
use_key = key
connected = True
else:
return await self.async_step_device(error="cant_get_token")
else:
dm = MiedaDevice(
name=device.get("name"),
device_id=user_input[CONF_DEVICE],
device_type=current_device.get(CONF_TYPE),
ip_address=current_device.get(CONF_IP_ADDRESS),
port=current_device.get(CONF_PORT),
token=use_token,
key=use_key,
protocol=2,
model=device.get(CONF_MODEL),
lua_file=None
)
if dm.connect():
connected = True
if not connected:
return await self.async_step_device(error="connect_error")
return self.async_create_entry(
title=device.get("name"),
data={
CONF_NAME: device.get("name"),
CONF_DEVICE_ID: user_input[CONF_DEVICE],
CONF_TYPE: current_device.get("type"),
CONF_PROTOCOL: current_device.get("protocol"),
CONF_IP_ADDRESS: current_device.get("ip_address"),
CONF_PORT: current_device.get("port"),
CONF_MODEL: device.get("model"),
CONF_TOKEN: use_token,
CONF_KEY: use_key,
"lua_file": file,
"sn": device.get("sn"),
"sn8": device.get("sn8"),
})
return await self.async_step_discover()
devices = await self._cloud.get_devices(self._current_home)
self._device_list = {}
device_list = {}
for device in devices:
if not self._device_configured(int(device.get("applianceCode"))):
self._device_list[int(device.get("applianceCode"))] = {
"device_id": int(device.get("applianceCode")),
"name": device.get("name"),
"type": int(device.get("type"), 16),
"sn8": device.get("sn8"),
@@ -198,6 +146,97 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
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["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["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.get_lua(self._device["sn"], self._device["type"], path, self._device["enterprise_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("protocol") == 3:
for byte_order_big in [False, True]:
token, key = await self._cloud.get_token(self._device.get("device_id"), byte_order_big=byte_order_big)
if token and key:
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=token,
key=key,
protocol=3,
model=None,
sn=None,
sn8=None,
lua_file=None
)
if dm.connect():
use_token = token
use_key = key
connected = True
else:
return await self.async_step_discover(error="cant_get_token")
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,
sn=None,
sn8=None,
lua_file=None
)
if dm.connect():
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("name"),
CONF_DEVICE_ID: self._device.get("device_id"),
CONF_TYPE: current_device.get("type"),
CONF_PROTOCOL: current_device.get("protocol"),
CONF_IP_ADDRESS: current_device.get("ip_address"),
CONF_PORT: current_device.get("port"),
CONF_MODEL: self._device.get("model"),
CONF_TOKEN: use_token,
CONF_KEY: use_key,
"lua_file": file,
"sn": self._device.get("sn"),
"sn8": self._device.get("sn8"),
})
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): str
}),
errors={"base": error} if error else None
)
class OptionsFlowHandler(config_entries.OptionsFlow):
def __init__(self, config_entry: config_entries.ConfigEntry):

View File

@@ -39,7 +39,9 @@ class MiedaDevice(threading.Thread):
token: str | None,
key: str | None,
protocol: int,
model: str,
model: str | None,
sn: str | None,
sn8: str | None,
lua_file: str | None):
threading.Thread.__init__(self)
self._socket = None
@@ -59,7 +61,8 @@ class MiedaDevice(threading.Thread):
self._is_run = False
self._device_protocol_version = 0
self._sub_type = None
self._sn = None
self._sn = sn
self._sn8 = sn8
self._attributes = {}
self._refresh_interval = 30
self._heartbeat_interval = 10
@@ -83,6 +86,14 @@ class MiedaDevice(threading.Thread):
def model(self):
return self._model
@property
def sn(self):
return self._sn
@property
def sn8(self):
return self._sn8
@property
def attributes(self):
return self._attributes

View File

@@ -9,7 +9,7 @@ class MideaEntity(Entity):
self._entity_key = entity_key
self._unique_id = f"{DOMAIN}.{self._device.device_id}_{entity_key}"
self.entity_id = self._unique_id
self._device_name = self._device.name
self._device_name = self._device.device_name
@property
def device(self):
@@ -19,7 +19,7 @@ class MideaEntity(Entity):
def device_info(self):
return {
"manufacturer": "Midea",
"model": self._device.model,
"model": f"{self._device.model} ({self._device.sn8})",
"identifiers": {(DOMAIN, self._device.device_id)},
"name": self._device_name
}

View File

@@ -1,6 +1,9 @@
{
"config": {
"error": {
"account_invalid": "登录美居失败,是否已修改过密码",
"invalid_input": "无效的输入请输入有效IP地址或auto",
"login_failed": "无法登录到美居,请检查用户名或密码",
"offline_error": "只能配置在线设备",
"download_lua_failed": "下载设备协议脚本失败",
"discover_failed": "无法在本地搜索到该设备",
@@ -19,17 +22,22 @@
"title": "登录"
},
"home": {
"description": "选择设备所在家庭",
"title": "家庭",
"data": {
"home": "设备所在家庭"
"home": "选择设备所在家庭"
}
},
"device": {
"description": "选择要添加的设备",
"title": "设备",
"data": {
"device": "要添加的设备"
"device": "选择要添加的设备"
}
},
"discover": {
"description": "获取设备信息,设备必须位于本地局域网内",
"title": "设备信息",
"data": {
"ip_address": "设备地址(输入auto自动搜索设备)"
}
}
}

View File

@@ -1,6 +1,9 @@
{
"config": {
"error": {
"account_invalid": "登录美居失败,是否已修改过密码",
"invalid_input": "无效的输入请输入有效IP地址或auto",
"login_failed": "无法登录到美居,请检查用户名或密码",
"offline_error": "只能配置在线设备",
"download_lua_failed": "下载设备协议脚本失败",
"discover_failed": "无法在本地搜索到该设备",
@@ -19,17 +22,22 @@
"title": "登录"
},
"home": {
"description": "选择设备所在家庭",
"title": "家庭",
"data": {
"home": "设备所在家庭"
"home": "选择设备所在家庭"
}
},
"device": {
"description": "选择要添加的设备",
"title": "设备",
"data": {
"device": "要添加的设备"
"device": "选择要添加的设备"
}
},
"discover": {
"description": "获取设备信息,设备必须位于本地局域网内",
"title": "设备信息",
"data": {
"ip_address": "设备地址(输入auto自动搜索设备)"
}
}
}