积分 208
金钱 190
威望 0
贡献 0
HASS币 0
中级会员
积分 208
金钱 190
HASS币 0
100 金钱
import asyncio
import datetime
import hashlib
import json
import logging
import subprocess
import threading
import time
import urllib.request
import websocket
import voluptuous as vol
from homeassistant.components.climate.const import (
HVACMode,
ClimateEntityFeature,
)
from homeassistant.const import CONF_FRIENDLY_NAME
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv, discovery
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.util.dt import utcnow
_LOGGER = logging.getLogger(__name__)
# 常量定义
CONF_LIFESMART_APPKEY = "appkey"
CONF_LIFESMART_APPTOKEN = "apptoken"
CONF_LIFESMART_USERTOKEN = "usertoken"
CONF_LIFESMART_USERNAME = "username"
CONF_LIFESMART_PASSWORD = "password"
CONF_LIFESMART_USERID = "userid"
CONF_EXCLUDE_ITEMS = "exclude"
SWITCH_TYPES = [
"SL_SF_RC",
"SL_SW_RC",
"SL_SW_IF3",
"SL_SF_IF3",
"SL_SW_CP3",
"SL_SW_RC3",
"SL_SW_IF2",
"SL_SF_IF2",
"SL_SW_CP2",
"SL_SW_FE2",
"SL_SW_RC2",
"SL_SW_ND2",
"SL_MC_ND2",
"SL_SW_IF1",
"SL_SF_IF1",
"SL_SW_CP1",
"SL_SW_FE1",
"SL_OL_W",
"SL_SW_RC1",
"SL_SW_ND1",
"SL_MC_ND1",
"SL_SW_ND3",
"SL_MC_ND3",
"SL_SW_ND2",
"SL_MC_ND2",
"SL_SW_ND1",
"SL_MC_ND1",
"SL_S",
"SL_SPWM",
"SL_P_SW",
"SL_SW_DM1",
"SL_SW_MJ2",
"SL_SW_MJ1",
"SL_OL",
"SL_OL_3C",
"SL_OL_DE",
"SL_OL_UK",
"SL_OL_UL",
"OD_WE_OT1",
"SL_NATURE",
]
LIGHT_SWITCH_TYPES = ["SL_OL_W", "SL_SW_IF1", "SL_SW_IF2", "SL_SW_IF3"]
QUANTUM_TYPES = ["OD_WE_QUAN"]
SPOT_TYPES = ["MSL_IRCTL", "OD_WE_IRCTL", "SL_SPOT"]
BINARY_SENSOR_TYPES = [
"SL_SC_G",
"SL_SC_BG",
"SL_SC_MHW",
"SL_SC_BM",
"SL_SC_CM",
"SL_P_A",
]
COVER_TYPES = ["SL_DOOYA"]
GAS_SENSOR_TYPES = ["SL_SC_WA", "SL_SC_CH", "SL_SC_CP", "ELIQ_EM"]
EV_SENSOR_TYPES = ["SL_SC_THL", "SL_SC_BE", "SL_SC_CQ"]
OT_SENSOR_TYPES = ["SL_SC_MHW", "SL_SC_BM", "SL_SC_G", "SL_SC_BG"]
LOCK_TYPES = ["SL_LK_LS", "SL_LK_GTM", "SL_LK_AG", "SL_LK_SG", "SL_LK_YL"]
SPEED_OFF = "Speed_Off"
SPEED_LOW = "Speed_Low"
SPEED_MEDIUM = "Speed_Medium"
SPEED_HIGH = "Speed_High"
LIFESMART_STATE_LIST = [
HVACMode.OFF,
HVACMode.AUTO,
HVACMode.FAN_ONLY,
HVACMode.COOL,
HVACMode.HEAT,
HVACMode.DRY,
]
CLIMATE_TYPES = ["V_AIR_P", "SL_CP_DN"]
ENTITY_ID = "entity_id"
DOMAIN = "lifesmart"
LifeSmart_STATE_MANAGER = "lifesmart_wss"
async def lifesmart_EpGetAll(appkey, apptoken, usertoken, userid):
url = "https://api.ilifesmart.com/app/api.EpGetAll"
tick = int(time.time())
sdata = f"method:EpGetAll,time:{tick},userid:{userid},usertoken:{usertoken},appkey:{appkey},apptoken:{apptoken}"
sign = hashlib.md5(sdata.encode(encoding='UTF-8')).hexdigest()
send_values = {
"id": 1,
"method": "EpGetAll",
"system": {
"ver": "1.0",
"lang": "zh",
"userid": userid,
"appkey": appkey,
"time": tick,
"sign": sign,
},
}
header = {"Content-Type": "application/json"}
send_data = json.dumps(send_values)
req = urllib.request.Request(url=url, data=send_data.encode('utf-8'), headers=header, method='POST')
response = json.loads(urllib.request.urlopen(req).read().decode('utf-8'))
if response['code'] == 0:
return response['message']
return False
def lifesmart_Sendkeys(appkey, apptoken, usertoken, userid, agt, ai, me, category, brand, keys):
url = "https://api.ilifesmart.com/app/irapi.SendKeys"
tick = int(time.time())
sdata = f"method:SendKeys,agt:{agt},ai:{ai},brand:{brand},category:{category},keys:{keys},me:{me},time:{tick},userid:{userid},usertoken:{usertoken},appkey:{appkey},apptoken:{apptoken}"
sign = hashlib.md5(sdata.encode(encoding='UTF-8')).hexdigest()
_LOGGER.debug(f"sendkey: {sdata}")
send_values = {
"id": 1,
"method": "SendKeys",
"params": {
"agt": agt,
"me": me,
"category": category,
"brand": brand,
"ai": ai,
"keys": keys,
},
"system": {
"ver": "1.0",
"lang": "zh",
"userid": userid,
"appkey": appkey,
"time": tick,
"sign": sign,
},
}
header = {"Content-Type": "application/json"}
send_data = json.dumps(send_values)
req = urllib.request.Request(url=url, data=send_data.encode('utf-8'), headers=header, method='POST')
response = json.loads(urllib.request.urlopen(req).read().decode('utf-8'))
_LOGGER.debug(f"sendkey_res: {response}")
return response
def lifesmart_Sendackeys(
appkey, apptoken, usertoken, userid, agt, ai, me, category, brand, keys, power, mode, temp, wind, swing
):
url = "https://api.ilifesmart.com/app/irapi.SendACKeys"
tick = int(time.time())
sdata = f"method:SendACKeys,agt:{agt},ai:{ai},brand:{brand},category:{category},keys:{keys},me:{me},mode:{mode},power:{power},swing:{swing},temp:{temp},wind:{wind},time:{tick},userid:{userid},usertoken:{usertoken},appkey:{appkey},apptoken:{apptoken}"
sign = hashlib.md5(sdata.encode(encoding='UTF-8')).hexdigest()
_LOGGER.debug(f"sendackey: {sdata}")
send_values = {
"id": 1,
"method": "SendACKeys",
"params": {
"agt": agt,
"me": me,
"category": category,
"brand": brand,
"ai": ai,
"keys": keys,
"power": power,
"mode": mode,
"temp": temp,
"wind": wind,
"swing": swing,
},
"system": {
"ver": "1.0",
"lang": "zh",
"userid": userid,
"appkey": appkey,
"time": tick,
"sign": sign,
},
}
header = {"Content-Type": "application/json"}
send_data = json.dumps(send_values)
req = urllib.request.Request(url=url, data=send_data.encode('utf-8'), headers=header, method='POST')
response = json.loads(urllib.request.urlopen(req).read().decode('utf-8'))
_LOGGER.debug(f"sendackey_res: {response}")
return response
def lifesmart_Login(uid, pwd, appkey):
url = "https://api.ilifesmart.com/app/auth.login"
login_data = {"uid": uid, "pwd": pwd, "appkey": appkey}
header = {"Content-Type": "application/json"}
req = urllib.request.Request(url=url, data=json.dumps(login_data).encode('utf-8'), headers=header, method='POST')
response = json.loads(urllib.request.urlopen(req).read().decode('utf-8'))
if 'userid' not in response:
_LOGGER.error("Login failed: 'userid' not found in response")
return None
return response
async def async_setup(hass, config):
uid = config['lifesmart']['uid']
pwd = config['lifesmart']['pwd']
appkey = config['lifesmart']['appkey']
res_login = lifesmart_Login(uid, pwd, appkey)
if res_login is None:
_LOGGER.error("Failed to login to lifesmart")
return False
if res_login['code'] == "error":
_LOGGER.error("Login failed: %s", res_login['msg'])
return False
# 组件的其他设置逻辑...
return True
def lifesmart_doAuth(userid, token, appkey, usertoken):
url = "https://api.ilifesmart.com/app/auth.do_auth"
auth_data = {
"userid": userid,
"token": token,
"appkey": appkey,
"rgn": "cn",
}
header = {"Content-Type": "application/json"}
req = urllib.request.Request(url=url, data=json.dumps(auth_data).encode('utf-8'), headers=header, method='POST')
response = json.loads(urllib.request.urlopen(req).read().decode('utf-8'))
return response
def setup(hass, config):
param = {
"appkey": config[DOMAIN][CONF_LIFESMART_APPKEY],
"apptoken": config[DOMAIN][CONF_LIFESMART_APPTOKEN],
"username": config[DOMAIN][CONF_LIFESMART_USERNAME],
"password": config[DOMAIN][CONF_LIFESMART_PASSWORD],
}
res_login = lifesmart_Login(param['username'], param['password'], param['appkey'])
if res_login['code'] == "error":
_LOGGER.error(f"login fail: {res_login['message']}")
return False
# 打印res_login的键
_LOGGER.debug(f"res_login keys: {res_login.keys()}")
param['token'] = res_login.get('token')
# 验证'userid'键是否存在
if 'userid' not in res_login:
_LOGGER.error("Login failed: 'userid' not found in response")
return False
param['userid'] = res_login['userid']
res_doauth = lifesmart_doAuth(param['userid'], param['token'], param['appkey'], param['usertoken'])
if res_doauth['code'] == "error":
_LOGGER.error(f"login fail: {res_doauth['message']}")
return False
param['usertoken'] = res_doauth.get('usertoken')
if not param['usertoken']:
_LOGGER.error("Login failed: 'usertoken' not found in response")
return False
exclude_items = config[DOMAIN][CONF_EXCLUDE_ITEMS]
devices = lifesmart_EpGetAll(param['appkey'], param['apptoken'], param['usertoken'], param['userid'])
for dev in devices:
from homeassistant.helpers import discovery
if dev['me'] in exclude_items:
continue
devtype = dev['devtype']
dev['agt'] = dev['agt'].replace("_", "")
if devtype in SWITCH_TYPES:
discovery.load_platform(hass, "switch", DOMAIN, {"dev": dev, "param": param}, config)
elif devtype in BINARY_SENSOR_TYPES:
discovery.load_platform(hass, "binary_sensor", DOMAIN, {"dev": dev, "param": param}, config)
elif devtype in COVER_TYPES:
discovery.load_platform(hass, "cover", DOMAIN, {"dev": dev, "param": param}, config)
elif devtype in SPOT_TYPES:
discovery.load_platform(hass, "light", DOMAIN, {"dev": dev, "param": param}, config)
elif devtype in CLIMATE_TYPES:
discovery.load_platform(hass, "climate", DOMAIN, {"dev": dev, "param": param}, config)
elif devtype in GAS_SENSOR_TYPES or devtype in EV_SENSOR_TYPES:
discovery.load_platform(hass, "sensor", DOMAIN, {"dev": dev, "param": param}, config)
if devtype in OT_SENSOR_TYPES:
discovery.load_platform(hass, "sensor", DOMAIN, {"dev": dev, "param": param}, config)
if devtype in LIGHT_SWITCH_TYPES:
discovery.load_platform(hass, "light", DOMAIN, {"dev": dev, "param": param}, config)
def send_keys(call):
agt = call.data['agt']
me = call.data['me']
ai = call.data['ai']
category = call.data['category']
brand = call.data['brand']
keys = call.data['keys']
restkey = lifesmart_Sendkeys(
param['appkey'], param['apptoken'], param['usertoken'], param['userid'], agt, ai, me, category, brand, keys
)
_LOGGER.debug(f"sendkey: {restkey}")
def send_ackeys(call):
agt = call.data['agt']
me = call.data['me']
ai = call.data['ai']
category = call.data['category']
brand = call.data['brand']
keys = call.data['keys']
power = call.data['power']
mode = call.data['mode']
temp = call.data['temp']
wind = call.data['wind']
swing = call.data['swing']
restackey = lifesmart_Sendackeys(
param['appkey'],
param['apptoken'],
param['usertoken'],
param['userid'],
agt,
ai,
me,
category,
brand,
keys,
power,
mode,
temp,
wind,
swing,
)
_LOGGER.debug(f"sendkey: {restackey}")
async def set_Event(msg):
if msg['msg']['idx'] != "s" and msg['msg']['me'] not in exclude_items:
devtype = msg['msg']['devtype']
agt = msg['msg']['agt'].replace("_","")
if devtype in SWITCH_TYPES and msg['msg']['idx'] in ["L1","L2","L3","P1","P2","P3"]:
enid = "switch."+(devtype + "_" + agt + "_" + msg['msg']['me'] + "_" + msg['msg']['idx']).lower()
attrs = hass.states.get(enid).attributes
if msg['msg']['type'] % 2 == 1:
hass.states.set(enid, 'on',attrs)
else:
hass.states.set(enid, 'off',attrs)
elif devtype in BINARY_SENSOR_TYPES and msg['msg']['idx'] in ["M","G","B","AXS","P1"]:
enid = "binary_sensor."+(devtype + "_" + agt + "_" + msg['msg']['me'] + "_" + msg['msg']['idx']).lower()
attrs = hass.states.get(enid).attributes
if msg['msg']['val'] == 1:
hass.states.set(enid, 'on',attrs)
else:
hass.states.set(enid, 'off',attrs)
elif devtype in COVER_TYPES and msg['msg']['idx'] == "P1":
enid = "cover."+(devtype + "_" + agt + "_" + msg['msg']['me']).lower()
attrs = dict(hass.states.get(enid).attributes)
nval = msg['msg']['val']
ntype = msg['msg']['type']
attrs['current_position'] = nval & 0x7F
_LOGGER.debug("websocket_cover_attrs: %s",str(attrs))
nstat = None
if ntype % 2 == 0:
if nval > 0:
nstat = "open"
else:
nstat = "closed"
else:
if nval & 0x80 == 0x80:
nstat = "opening"
else:
nstat = "closing"
hass.states.set(enid, nstat, attrs)
elif devtype in EV_SENSOR_TYPES:
enid = "sensor."+(devtype + "_" + agt + "_" + msg['msg']['me'] + "_" + msg['msg']['idx']).lower()
attrs = hass.states.get(enid).attributes
hass.states.set(enid, msg['msg']['v'], attrs)
elif devtype in GAS_SENSOR_TYPES and msg['msg']['val'] > 0:
enid = "sensor."+(devtype + "_" + agt + "_" + msg['msg']['me'] + "_" + msg['msg']['idx']).lower()
attrs = hass.states.get(enid).attributes
hass.states.set(enid, msg['msg']['val'], attrs)
elif devtype in SPOT_TYPES or devtype in LIGHT_SWITCH_TYPES:
enid = "light."+(devtype + "_" + agt + "_" + msg['msg']['me'] + "_" + msg['msg']['idx']).lower()
attrs = hass.states.get(enid).attributes
if msg['msg']['type'] % 2 == 1:
hass.states.set(enid, 'on',attrs)
else:
hass.states.set(enid, 'off',attrs)
#elif devtype in QUANTUM_TYPES and msg['msg']['idx'] == "P1":
# enid = "light."+(devtype + "_" + agt + "_" + msg['msg']['me'] + "_P1").lower()
# attrs = hass.states.get(enid).attributes
# hass.states.set(enid, msg['msg']['val'], attrs)
elif devtype in CLIMATE_TYPES:
enid = "climate."+(devtype + "_" + agt + "_" + msg['msg']['me']).lower().replace(":","_").replace("@","_")
#climate.v_air_p_a3yaaabbaegdrzcznti3mg_8ae5_1_2_1
_idx = msg['msg']['idx']
attrs = dict(hass.states.get(enid).attributes)
nstat = hass.states.get(enid).state
_LOGGER.info("enid: %s",str(enid))
_LOGGER.info("_idx: %s",str(_idx))
_LOGGER.info("attrs: %s",str(attrs))
_LOGGER.info("nstat: %s",str(nstat))
if _idx == "O":
if msg['msg']['type'] % 2 == 1:
nstat = attrs['last_mode']
hass.states.set(enid, nstat, attrs)
else:
nstat = HVACMode.OFF
hass.states.set(enid, nstat, attrs)
if _idx == "P1":
if msg['msg']['type'] % 2 == 1:
nstat = HVACMode.HEAT.value
hass.states.set(enid, nstat, attrs)
else:
nstat = HVACMode.OFF
hass.states.set(enid, nstat, attrs)
if _idx == "P2":
if msg['msg']['type'] % 2 == 1:
attrs['Heating'] = "true"
hass.states.set(enid, nstat, attrs)
else:
attrs['Heating'] = "false"
hass.states.set(enid, nstat, attrs)
elif _idx == "MODE":
if msg['msg']['type'] == 206:
if nstat != HVACMode.OFF:
nstat = LIFESMART_STATE_LIST[msg['msg']['val']]
attrs['last_mode'] = LIFESMART_STATE_LIST[msg['msg']['val']]
hass.states.set(enid, nstat, attrs)
elif _idx == "F":
if msg['msg']['type'] == 206:
attrs['fan_mode'] = get_fan_mode(msg['msg']['val'])
hass.states.set(enid, nstat, attrs)
elif _idx == "tT" or _idx == "P3":
if msg['msg']['type'] == 136:
attrs['temperature'] = msg['msg']['v']
hass.states.set(enid, nstat, attrs)
elif _idx == "T" or _idx == "P4":
if msg['msg']['type'] == 8 or msg['msg']['type'] == 9:
attrs['current_temperature'] = msg['msg']['v']
hass.states.set(enid, nstat, attrs)
elif devtype in LOCK_TYPES:
if msg['msg']['idx'] == "BAT":
enid = "sensor."+(devtype + "_" + agt + "_" + msg['msg']['me'] + "_" + msg['msg']['idx']).lower()
attrs = hass.states.get(enid).attributes
hass.states.set(enid, msg['msg']['val'], attrs)
elif msg['msg']['idx'] == "EVTLO":
enid = "binary_sensor."+(devtype + "_" + agt + "_" + msg['msg']['me'] + "_" + msg['msg']['idx']).lower()
val = msg['msg']['val']
ulk_way = val >> 12
ulk_user = val & 0xfff
ulk_success = True
if ulk_user == 0:
ulk_success = False
attrs = {"unlocking_way": ulk_way,"unlocking_user": ulk_user,"devtype": devtype,"unlocking_success": ulk_success,"last_time": datetime.datetime.fromtimestamp(msg['msg']['ts']/1000).strftime("%Y-%m-%d %H:%M:%S") }
if msg['msg']['type'] % 2 == 1:
hass.states.set(enid, 'on',attrs)
else:
hass.states.set(enid, 'off',attrs)
if devtype in OT_SENSOR_TYPES and msg['msg']['idx'] in ["Z","V","P3","P4"]:
enid = "sensor."+(devtype + "_" + agt + "_" + msg['msg']['me'] + "_" + msg['msg']['idx']).lower()
attrs = hass.states.get(enid).attributes
hass.states.set(enid, msg['msg']['v'], attrs)
def on_message(ws, message):
_LOGGER.info(f"websocket_msg: {message}")
msg = json.loads(message)
if 'type' not in msg:
return
if msg['type'] != "io":
return
hass.async_create_task(set_Event(msg))
def on_error(ws, error):
_LOGGER.debug(f"websocket_error: {error}")
def on_close(ws):
_LOGGER.debug("lifesmart websocket closed...")
def on_open(ws):
tick = int(time.time())
sdata = f"method:WbAuth,time:{tick},userid:{param['userid']},usertoken:{param['usertoken']},appkey:{param['appkey']},apptoken:{param['apptoken']}"
sign = hashlib.md5(sdata.encode(encoding='UTF-8')).hexdigest()
send_values = {
"id": 1,
"method": "WbAuth",
"system": {
"ver": "1.0",
"lang": "zh",
"userid": param['userid'],
"appkey": param['appkey'],
"time": tick,
"sign": sign,
},
}
header = {"Content-Type": "application/json"}
send_data = json.dumps(send_values)
ws.send(send_data)
_LOGGER.debug("lifesmart websocket sending_data...")
hass.services.async_register(DOMAIN, "send_keys", send_keys)
hass.services.async_register(DOMAIN, "send_ackeys", send_ackeys)
ws = websocket.WebSocketApp(
"wss://api.ilifesmart.com:8443/wsapp/",
on_message=on_message,
on_error=on_error,
on_close=on_close,
)
ws.on_open = on_open
hass.data[LifeSmart_STATE_MANAGER] = LifeSmartStatesManager(ws)
hass.data[LifeSmart_STATE_MANAGER].start_keep_alive()
return True
class LifeSmartEntity(Entity):
"""LifeSmart base device."""
def __init__(self, dev, idx, val, param):
"""Initialize the switch."""
self._name = f"{dev['name']}_{idx}"
self._appkey = param['appkey']
self._apptoken = param['apptoken']
self._usertoken = param['usertoken']
self._userid = param['userid']
self._agt = dev['agt']
self._me = dev['me']
self._idx = idx
self._devtype = dev['devtype']
attrs = {"agt": self._agt, "me": self._me, "idx": self._idx, "devtype": self._devtype}
self._attributes = attrs
@property
def object_id(self):
"""Return LifeSmart device id."""
return self.entity_id
@property
def state_attrs(self):
"""Return the state attributes."""
return self._attributes
@property
def extra_state_attributes(self):
"""Return the extra state attributes of the device."""
return self._attributes
@property
def name(self):
"""Return LifeSmart device name."""
return self._name
@property
def assumed_state(self):
"""Return true if we do optimistic updates."""
return False
@property
def should_poll(self):
"""check with the entity for an updated state."""
return False
@staticmethod
def _lifesmart_epset(self, type, val, idx):
url = "https://api.ilifesmart.com/app/api.EpSet"
tick = int(time.time())
appkey = self._appkey
apptoken = self._apptoken
userid = self._userid
usertoken = self._usertoken
agt = self._agt
me = self._me
sdata = f"method:EpSet,agt:{agt},idx:{idx},me:{me},type:{type},val:{val},time:{tick},userid:{userid},usertoken:{usertoken},appkey:{appkey},apptoken:{apptoken}"
sign = hashlib.md5(sdata.encode(encoding='UTF-8')).hexdigest()
send_values = {
"id": 1,
"method": "EpSet",
"system": {
"ver": "1.0",
"lang": "zh",
"userid": userid,
"appkey": appkey,
"time": tick,
"sign": sign,
},
"params": {
"agt": agt,
"me": me,
"idx": idx,
"type": type,
"val": val,
},
}
header = {"Content-Type": "application/json"}
send_data = json.dumps(send_values)
req = urllib.request.Request(url=url, data=send_data.encode('utf-8'), headers=header, method='POST')
response = json.loads(urllib.request.urlopen(req).read().decode('utf-8'))
_LOGGER.info(f"epset_send: {send_data}")
_LOGGER.info(f"epset_res: {response}")
return response['code']
@staticmethod
def _lifesmart_epget(self):
url = "https://api.ilifesmart.com/app/api.EpGet"
tick = int(time.time())
appkey = self._appkey
apptoken = self._apptoken
userid = self._userid
usertoken = self._usertoken
agt = self._agt
me = self._me
sdata = f"method:EpGet,agt:{agt},me:{me},time:{tick},userid:{userid},usertoken:{usertoken},appkey:{appkey},apptoken:{apptoken}"
sign = hashlib.md5(sdata.encode(encoding='UTF-8')).hexdigest()
send_values = {
"id": 1,
"method": "EpGet",
"system": {
"ver": "1.0",
"lang": "zh",
"userid": userid,
"appkey": appkey,
"time": tick,
"sign": sign,
},
"params": {
"agt": agt,
"me": me,
},
}
header = {"Content-Type": "application/json"}
send_data = json.dumps(send_values)
req = urllib.request.Request(url=url, data=send_data.encode('utf-8'), headers=header, method='POST')
response = json.loads(urllib.request.urlopen(req).read().decode('utf-8'))
return response['message']['data']
class LifeSmartStatesManager(threading.Thread):
def __init__(self, ws):
threading.Thread.__init__(self)
self._run = False
self._lock = threading.Lock()
self._ws = ws
def run(self):
while self._run:
_LOGGER.debug('lifesmart: starting wss...')
self._ws.run_forever()
_LOGGER.debug('lifesmart: restart wss...')
time.sleep(10)
def start_keep_alive(self):
with self._lock:
self._run = True
threading.Thread.start(self)
def stop_keep_alive(self):
with self._lock:
self._run = False
self.join()
返回的错误日志是:Logger: homeassistant.setup
Source: setup.py:333
First occurred: 22:26:02 (1 occurrences)
Last logged: 22:26:02
Error during setup of component lifesmart
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/setup.py", line 333, in _async_setup_component
result = await task
^^^^^^^^^^
File "/config/custom_components/lifesmart/__init__.py", line 221, in async_setup
uid = config['lifesmart']['uid']
~~~~~~~~~~~~~~~~~~~^^^^^^^
KeyError: 'uid'
我来回答