mirror of
https://github.com/sususweet/midea-meiju-codec.git
synced 2025-09-27 18:22:41 +00:00
优化设备添加流程
This commit is contained in:
@@ -56,6 +56,8 @@ async def async_setup_entry(hass: HomeAssistant, config_entry):
|
|||||||
port = config_entry.data.get(CONF_PORT)
|
port = config_entry.data.get(CONF_PORT)
|
||||||
model = config_entry.data.get(CONF_MODEL)
|
model = config_entry.data.get(CONF_MODEL)
|
||||||
protocol = config_entry.data.get(CONF_PROTOCOL)
|
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")
|
lua_file = config_entry.data.get("lua_file")
|
||||||
_LOGGER.error(f"lua_file = {lua_file}")
|
_LOGGER.error(f"lua_file = {lua_file}")
|
||||||
if protocol == 3 and (key is None or key is None):
|
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,
|
key=key,
|
||||||
protocol=protocol,
|
protocol=protocol,
|
||||||
model=model,
|
model=model,
|
||||||
|
sn=sn,
|
||||||
|
sn8=sn8,
|
||||||
lua_file=lua_file,
|
lua_file=lua_file,
|
||||||
)
|
)
|
||||||
if device:
|
if device:
|
||||||
|
@@ -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_id = config_entry.data.get(CONF_DEVICE_ID)
|
||||||
device = hass.data[DOMAIN][DEVICES].get(device_id)
|
device = hass.data[DOMAIN][DEVICES].get(device_id)
|
||||||
binary_sensors = []
|
binary_sensors = []
|
||||||
sensor = MideaDeviceStatusSensor(device, "online")
|
sensor = MideaDeviceStatusSensor(device, "status")
|
||||||
binary_sensors.append(sensor)
|
binary_sensors.append(sensor)
|
||||||
async_add_entities(binary_sensors)
|
async_add_entities(binary_sensors)
|
||||||
|
|
||||||
@@ -32,6 +32,14 @@ class MideaDeviceStatusSensor(MideaEntity):
|
|||||||
def state(self):
|
def state(self):
|
||||||
return STATE_ON if self._device.connected else STATE_OFF
|
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
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
return self.state == STATE_ON
|
return self.state == STATE_ON
|
||||||
@@ -46,8 +54,6 @@ class MideaDeviceStatusSensor(MideaEntity):
|
|||||||
|
|
||||||
def update_state(self, status):
|
def update_state(self, status):
|
||||||
try:
|
try:
|
||||||
_LOGGER.debug("=" * 50)
|
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
_LOGGER.debug("-" * 50)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
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.const import (
|
from homeassistant.const import (
|
||||||
@@ -15,7 +16,6 @@ from homeassistant.const import (
|
|||||||
CONF_PROTOCOL,
|
CONF_PROTOCOL,
|
||||||
CONF_TOKEN,
|
CONF_TOKEN,
|
||||||
CONF_NAME
|
CONF_NAME
|
||||||
|
|
||||||
)
|
)
|
||||||
from .core.cloud import MeijuCloudExtend
|
from .core.cloud import MeijuCloudExtend
|
||||||
from .core.discover import discover
|
from .core.discover import discover
|
||||||
@@ -36,6 +36,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
_cloud = None
|
_cloud = None
|
||||||
_current_home = None
|
_current_home = None
|
||||||
_device_list = {}
|
_device_list = {}
|
||||||
|
_device = None
|
||||||
|
|
||||||
def _get_configured_account(self):
|
def _get_configured_account(self):
|
||||||
for entry in self._async_current_entries():
|
for entry in self._async_current_entries():
|
||||||
@@ -49,23 +50,39 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
return True
|
return True
|
||||||
return False
|
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=None, error=None):
|
||||||
|
if self._session is None:
|
||||||
|
self._session = async_create_clientsession(self.hass)
|
||||||
username, password = self._get_configured_account()
|
username, password = self._get_configured_account()
|
||||||
if username is not None and password is not None:
|
if username is not None and password is not None:
|
||||||
if self._session is None:
|
|
||||||
self._session = async_create_clientsession(self.hass)
|
|
||||||
if self._cloud is None:
|
if self._cloud is None:
|
||||||
self._cloud = MeijuCloudExtend(self._session, username, password)
|
self._cloud = MeijuCloudExtend(self._session, username, password)
|
||||||
if await self._cloud.login():
|
if await self._cloud.login():
|
||||||
return await self.async_step_home()
|
return await self.async_step_home()
|
||||||
|
else:
|
||||||
|
return await self.async_step_user(error="account_invalid")
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
return self.async_create_entry(
|
if self._cloud is None:
|
||||||
title=f"{user_input[CONF_USERNAME]}",
|
self._cloud = MeijuCloudExtend(self._session, user_input[CONF_USERNAME], user_input[CONF_PASSWORD])
|
||||||
data={
|
if await self._cloud.login():
|
||||||
CONF_TYPE: CONF_ACCOUNT,
|
return self.async_create_entry(
|
||||||
CONF_USERNAME: user_input[CONF_USERNAME],
|
title=f"{user_input[CONF_USERNAME]}",
|
||||||
CONF_PASSWORD: user_input[CONF_PASSWORD]
|
data={
|
||||||
})
|
CONF_TYPE: CONF_ACCOUNT,
|
||||||
|
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(
|
return self.async_show_form(
|
||||||
step_id="user",
|
step_id="user",
|
||||||
data_schema=vol.Schema({
|
data_schema=vol.Schema({
|
||||||
@@ -96,86 +113,17 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
# 下载lua
|
# 下载lua
|
||||||
# 本地尝试连接设备
|
# 本地尝试连接设备
|
||||||
device = self._device_list[user_input[CONF_DEVICE]]
|
self._device = self._device_list[user_input[CONF_DEVICE]]
|
||||||
if not device.get("online"):
|
if not self._device.get("online"):
|
||||||
return await self.async_step_device(error="offline_error")
|
return await self.async_step_device(error="offline_error")
|
||||||
discover_devices = discover([device["type"]])
|
return await self.async_step_discover()
|
||||||
_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"),
|
|
||||||
})
|
|
||||||
devices = await self._cloud.get_devices(self._current_home)
|
devices = await self._cloud.get_devices(self._current_home)
|
||||||
self._device_list = {}
|
self._device_list = {}
|
||||||
device_list = {}
|
device_list = {}
|
||||||
for device in devices:
|
for device in devices:
|
||||||
if not self._device_configured(int(device.get("applianceCode"))):
|
if not self._device_configured(int(device.get("applianceCode"))):
|
||||||
self._device_list[int(device.get("applianceCode"))] = {
|
self._device_list[int(device.get("applianceCode"))] = {
|
||||||
|
"device_id": int(device.get("applianceCode")),
|
||||||
"name": device.get("name"),
|
"name": device.get("name"),
|
||||||
"type": int(device.get("type"), 16),
|
"type": int(device.get("type"), 16),
|
||||||
"sn8": device.get("sn8"),
|
"sn8": device.get("sn8"),
|
||||||
@@ -198,7 +146,98 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
errors={"base": error} if error else None
|
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):
|
class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
def __init__(self, config_entry: config_entries.ConfigEntry):
|
def __init__(self, config_entry: config_entries.ConfigEntry):
|
||||||
pass
|
pass
|
||||||
|
@@ -39,7 +39,9 @@ class MiedaDevice(threading.Thread):
|
|||||||
token: str | None,
|
token: str | None,
|
||||||
key: str | None,
|
key: str | None,
|
||||||
protocol: int,
|
protocol: int,
|
||||||
model: str,
|
model: str | None,
|
||||||
|
sn: str | None,
|
||||||
|
sn8: str | None,
|
||||||
lua_file: str | None):
|
lua_file: str | None):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
self._socket = None
|
self._socket = None
|
||||||
@@ -59,7 +61,8 @@ class MiedaDevice(threading.Thread):
|
|||||||
self._is_run = False
|
self._is_run = False
|
||||||
self._device_protocol_version = 0
|
self._device_protocol_version = 0
|
||||||
self._sub_type = None
|
self._sub_type = None
|
||||||
self._sn = None
|
self._sn = sn
|
||||||
|
self._sn8 = sn8
|
||||||
self._attributes = {}
|
self._attributes = {}
|
||||||
self._refresh_interval = 30
|
self._refresh_interval = 30
|
||||||
self._heartbeat_interval = 10
|
self._heartbeat_interval = 10
|
||||||
@@ -83,6 +86,14 @@ class MiedaDevice(threading.Thread):
|
|||||||
def model(self):
|
def model(self):
|
||||||
return self._model
|
return self._model
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sn(self):
|
||||||
|
return self._sn
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sn8(self):
|
||||||
|
return self._sn8
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def attributes(self):
|
def attributes(self):
|
||||||
return self._attributes
|
return self._attributes
|
||||||
|
@@ -9,7 +9,7 @@ class MideaEntity(Entity):
|
|||||||
self._entity_key = entity_key
|
self._entity_key = entity_key
|
||||||
self._unique_id = f"{DOMAIN}.{self._device.device_id}_{entity_key}"
|
self._unique_id = f"{DOMAIN}.{self._device.device_id}_{entity_key}"
|
||||||
self.entity_id = self._unique_id
|
self.entity_id = self._unique_id
|
||||||
self._device_name = self._device.name
|
self._device_name = self._device.device_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device(self):
|
def device(self):
|
||||||
@@ -19,7 +19,7 @@ class MideaEntity(Entity):
|
|||||||
def device_info(self):
|
def device_info(self):
|
||||||
return {
|
return {
|
||||||
"manufacturer": "Midea",
|
"manufacturer": "Midea",
|
||||||
"model": self._device.model,
|
"model": f"{self._device.model} ({self._device.sn8})",
|
||||||
"identifiers": {(DOMAIN, self._device.device_id)},
|
"identifiers": {(DOMAIN, self._device.device_id)},
|
||||||
"name": self._device_name
|
"name": self._device_name
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"error": {
|
"error": {
|
||||||
|
"account_invalid": "登录美居失败,是否已修改过密码",
|
||||||
|
"invalid_input": "无效的输入,请输入有效IP地址或auto",
|
||||||
|
"login_failed": "无法登录到美居,请检查用户名或密码",
|
||||||
"offline_error": "只能配置在线设备",
|
"offline_error": "只能配置在线设备",
|
||||||
"download_lua_failed": "下载设备协议脚本失败",
|
"download_lua_failed": "下载设备协议脚本失败",
|
||||||
"discover_failed": "无法在本地搜索到该设备",
|
"discover_failed": "无法在本地搜索到该设备",
|
||||||
@@ -19,17 +22,22 @@
|
|||||||
"title": "登录"
|
"title": "登录"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"description": "选择设备所在家庭",
|
|
||||||
"title": "家庭",
|
"title": "家庭",
|
||||||
"data": {
|
"data": {
|
||||||
"home": "设备所在家庭"
|
"home": "选择设备所在家庭"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"device": {
|
"device": {
|
||||||
"description": "选择要添加的设备",
|
|
||||||
"title": "设备",
|
"title": "设备",
|
||||||
"data": {
|
"data": {
|
||||||
"device": "要添加的设备"
|
"device": "选择要添加的设备"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"discover": {
|
||||||
|
"description": "获取设备信息,设备必须位于本地局域网内",
|
||||||
|
"title": "设备信息",
|
||||||
|
"data": {
|
||||||
|
"ip_address": "设备地址(输入auto自动搜索设备)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"error": {
|
"error": {
|
||||||
|
"account_invalid": "登录美居失败,是否已修改过密码",
|
||||||
|
"invalid_input": "无效的输入,请输入有效IP地址或auto",
|
||||||
|
"login_failed": "无法登录到美居,请检查用户名或密码",
|
||||||
"offline_error": "只能配置在线设备",
|
"offline_error": "只能配置在线设备",
|
||||||
"download_lua_failed": "下载设备协议脚本失败",
|
"download_lua_failed": "下载设备协议脚本失败",
|
||||||
"discover_failed": "无法在本地搜索到该设备",
|
"discover_failed": "无法在本地搜索到该设备",
|
||||||
@@ -19,17 +22,22 @@
|
|||||||
"title": "登录"
|
"title": "登录"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"description": "选择设备所在家庭",
|
|
||||||
"title": "家庭",
|
"title": "家庭",
|
||||||
"data": {
|
"data": {
|
||||||
"home": "设备所在家庭"
|
"home": "选择设备所在家庭"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"device": {
|
"device": {
|
||||||
"description": "选择要添加的设备",
|
|
||||||
"title": "设备",
|
"title": "设备",
|
||||||
"data": {
|
"data": {
|
||||||
"device": "要添加的设备"
|
"device": "选择要添加的设备"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"discover": {
|
||||||
|
"description": "获取设备信息,设备必须位于本地局域网内",
|
||||||
|
"title": "设备信息",
|
||||||
|
"data": {
|
||||||
|
"ip_address": "设备地址(输入auto自动搜索设备)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user