Open hellojazy opened 5 months ago
另外发现插座也不能工作。设备类型为OD_WE_OT1
DOOYA 电机应该是正常支持了的,请问集成里面是没有这个设备吗?
对的 没有这个设备 ,除了流光开关有。像人体感应、无线插座、窗帘电机都没同步过来。
获取 Outlook for iOShttps://aka.ms/o0ukef
发件人: Maple Gao @.> 发送时间: Monday, June 3, 2024 12:14:52 PM 收件人: MapleEve/lifesmart-for-homeassistant @.> 抄送: hellojazy @.>; Author @.> 主题: Re: [MapleEve/lifesmart-for-homeassistant] 杜亚窗帘电机没有吗? SL_DOOYA_V1 (Issue #6)
DOOYA 电机应该是正常支持了的,请问集成里面是没有这个设备吗?
― Reply to this email directly, view it on GitHubhttps://github.com/MapleEve/lifesmart-for-homeassistant/issues/6#issuecomment-2144242430, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AZYDWOGR22COIJXDWO3CERDZFPUTZAVCNFSM6AAAAABIJ4P7AWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNBUGI2DENBTGA. You are receiving this because you authored the thread.Message ID: @.***>
对的 没有这个设备 ,除了流光开关有。像人体感应、无线插座、窗帘电机都没同步过来。 获取 Outlook for iOShttps://aka.ms/o0ukef … ____ 发件人: Maple Gao @.> 发送时间: Monday, June 3, 2024 12:14:52 PM 收件人: MapleEve/lifesmart-for-homeassistant @.> 抄送: hellojazy @.>; Author @.> 主题: Re: [MapleEve/lifesmart-for-homeassistant] 杜亚窗帘电机没有吗? SL_DOOYA_V1 (Issue #6) DOOYA 电机应该是正常支持了的,请问集成里面是没有这个设备吗? ― Reply to this email directly, view it on GitHub<#6 (comment)>, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AZYDWOGR22COIJXDWO3CERDZFPUTZAVCNFSM6AAAAABIJ4P7AWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNBUGI2DENBTGA. You are receiving this because you authored the thread.Message ID: @.***>
了解了,我看下是不是窗帘那块写 bug 了,因为有使用最新的那个窗帘电机的反馈是 OK 的
我修复了一下错误,现在可以用了 """Support for LifeSmart covers. by @MapleEve"""
import logging import asyncio
from homeassistant.components.cover import ( ATTR_POSITION, CoverEntity, CoverEntityFeature, CoverDeviceClass, ) from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo
from . import LifeSmartDevice, generate_entity_id from .const import ( DOMAIN, DEVICE_ID_KEY, DEVICE_TYPE_KEY, DEVICE_DATA_KEY, DEVICE_NAME_KEY, HUB_ID_KEY, COVER_TYPES, LIFESMART_SIGNAL_UPDATE_ENTITY, MANUFACTURER, )
_LOGGER = logging.getLogger(name)
async def async_setup_entry(hass, config_entry, async_add_entities):
devices = hass.data[DOMAIN][config_entry.entry_id]["devices"]
exclude_devices = hass.data[DOMAIN][config_entry.entry_id]["exclude_devices"]
exclude_hubs = hass.data[DOMAIN][config_entry.entry_id]["exclude_hubs"]
client = hass.data[DOMAIN][config_entry.entry_id]["client"]
cover_devices = []
for device in devices:
if (
device[DEVICE_ID_KEY] in exclude_devices
or device[HUB_ID_KEY] in exclude_hubs
):
continue
device_type = device[DEVICE_TYPE_KEY]
if device_type in COVER_TYPES:
ha_device = LifeSmartDevice(device, client)
if device_type in ["SL_CN_IF", "SL_CN_FE"]:
idx = ["P1", "P2", "P3"]
val = {i: device[DEVICE_DATA_KEY][i] for i in idx}
elif device_type == "SL_P_V2":
idx = ["P2", "P3", "P4", "P8"] # 增加了P8电量属性
val = {i: device[DEVICE_DATA_KEY][i] for i in idx}
elif device_type == "SL_SW_WIN":
idx = ["OP", "CL", "ST"]
val = {i: device[DEVICE_DATA_KEY][i] for i in idx}
elif device_type == "SL_DOOYA":
idx = "P1"
val = device[DEVICE_DATA_KEY][idx]
else:
continue
cover_devices.append(LifeSmartCover(ha_device, device, idx, val, client))
async_add_entities(cover_devices)
class LifeSmartCover(LifeSmartDevice, CoverEntity): """LifeSmart cover devices."""
def __init__(self, device, raw_device_data, idx, val, client):
"""Init LifeSmart cover device."""
super().__init__(raw_device_data, client)
device_name = raw_device_data[DEVICE_NAME_KEY]
device_type = raw_device_data[DEVICE_TYPE_KEY]
hub_id = raw_device_data[HUB_ID_KEY]
device_id = raw_device_data[DEVICE_ID_KEY]
self._attr_has_entity_name = True
self.device_name = device_name
self.sensor_device_name = raw_device_data[DEVICE_NAME_KEY]
self.device_id = device_id
self.hub_id = hub_id
self.device_type = device_type
self.raw_device_data = raw_device_data
self._device = device
self.entity_id = generate_entity_id(device_type, hub_id, device_id, idx)
self._client = client
self._attr_device_class = CoverDeviceClass.CURTAIN
self._supported_features = (
CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE
| CoverEntityFeature.STOP # 默认支持打开、关闭和停止
)
self._is_opening = False
self._is_closing = False
self._is_closed = False
if device_type in COVER_TYPES:
if device_type == "SL_DOOYA":
self._pos = val["val"]
self._open_cmd = {"type": "0xCF", "val": 100, "idx": "P2"}
self._close_cmd = {"type": "0xCF", "val": 0, "idx": "P2"}
self._stop_cmd = {"type": "0xCE", "val": 0x80, "idx": "P2"}
self._position_cmd = {"type": "0xCF", "idx": "P2"}
self._supported_features |= (
CoverEntityFeature.SET_POSITION
) # 额外支持设置位置
elif device_type == "SL_P_V2":
self._open_cmd = {"type": "0x81", "val": 1, "idx": "P2"}
self._close_cmd = {"type": "0x81", "val": 1, "idx": "P3"}
self._stop_cmd = {"type": "0x81", "val": 1, "idx": "P4"}
self._attr_battery_level = val["P8"]["v"]
elif device_type == "SL_SW_WIN":
self._open_cmd = {"type": "0x81", "val": 1, "idx": "OP"}
self._close_cmd = {"type": "0x81", "val": 1, "idx": "CL"}
self._stop_cmd = {"type": "0x81", "val": 1, "idx": "ST"}
elif device_type in ["SL_CN_IF", "SL_CN_FE"]:
self._open_cmd = {"type": "0x81", "val": 1, "idx": "P1"}
self._close_cmd = {"type": "0x81", "val": 1, "idx": "P2"}
self._stop_cmd = {"type": "0x81", "val": 1, "idx": "P3"}
@property
def current_cover_position(self):
"""Return the current position of the cover."""
if CoverEntityFeature.SET_POSITION:
return self._pos
else:
return None
@property
def device_info(self) -> DeviceInfo:
"""Return the device info."""
return DeviceInfo(
identifiers={(DOMAIN, self.hub_id, self.device_id)},
name=self.sensor_device_name,
manufacturer=MANUFACTURER,
model=self.device_type,
sw_version=self.raw_device_data["ver"],
via_device=(DOMAIN, self.hub_id),
)
@property
def device_class(self):
"""Return the class of binary sensor."""
return self._attr_device_class
@property
def unique_id(self):
"""A unique identifier for this entity."""
return self.entity_id
@property
def is_closed(self):
"""Return if the cover is closed."""
if self.device_type == "SL_DOOYA":
return self.current_cover_position <= 0
else:
return self._is_closed
async def async_close_cover(self, **kwargs):
"""Close the cover."""
await super().async_lifesmart_epset(
self._close_cmd["type"], self._close_cmd["val"], self._close_cmd["idx"]
)
self.schedule_update_ha_state()
await self.async_poll_status()
async def async_open_cover(self, **kwargs):
"""Open the cover."""
await super().async_lifesmart_epset(
self._open_cmd["type"], self._open_cmd["val"], self._open_cmd["idx"]
)
self.schedule_update_ha_state()
await self.async_poll_status()
async def async_stop_cover(self, **kwargs):
"""Stop the cover."""
if not self._supported_features & CoverEntityFeature.STOP:
return
await super().async_lifesmart_epset(
self._stop_cmd["type"], self._stop_cmd["val"], self._stop_cmd["idx"]
)
self.schedule_update_ha_state()
async def async_toggle(self, **kwargs):
"""Toggle the entity."""
if self.is_opening or self.is_closing:
await self.async_stop_cover()
elif self.is_closed:
await self.async_open_cover()
else:
await self.async_close_cover()
async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
if not self._supported_features & CoverEntityFeature.SET_POSITION:
return
position = kwargs.get(ATTR_POSITION)
await super().async_lifesmart_epset(
self._position_cmd["type"], position, self._position_cmd["idx"]
)
self.schedule_update_ha_state()
await self.async_poll_status()
async def async_poll_status(self):
"""Poll cover status until it stops moving."""
max_polls = 40 # 最大轮询次数40
poll_interval = 0.8 # 轮询间隔0.5秒
for _ in range(max_polls):
if not self._is_opening and not self._is_closing:
break # 如果已停止,跳出循环
await asyncio.sleep(poll_interval)
await self.async_lifesmart_epget()
self.schedule_update_ha_state()
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
await super().async_added_to_hass()
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{LIFESMART_SIGNAL_UPDATE_ENTITY}_{self.entity_id}",
self._update_state,
)
)
self.async_schedule_update_ha_state(force_refresh=True)
async def _update_state(self, data) -> None:
"""Update cover state."""
if data is not None:
if self.device_type == "SL_DOOYA":
if isinstance(data, dict): # 判断data是否为字典类型
pos = data["val"] & 0x7F
if pos <= 100:
self._pos = pos
self._state = self._pos
else:
self._pos = None
self._state = None
if data["type"] & 0x01 == 1: # 正在运行
if data["val"] & 0x80 == 0x80:
self._is_opening = True
self._is_closing = False
else:
self._is_opening = False
self._is_closing = True
else: # 没有运行
self._is_opening = False
self._is_closing = False
if self._pos == 100:
self._is_closed = False
elif self._pos == 0:
self._is_closed = True
else:
self._pos = None
self._state = None
self._is_opening = False
self._is_closing = False
self._is_closed = None # 无法判断窗帘状态
self.schedule_update_ha_state()
同时 switch 也有版本号报错。 def device_info(self) -> DeviceInfo: """Return the device info.""" sw_version = self.raw_device_data.get("ver", "unknown") # 使用 get 方法提供默认版本号 return DeviceInfo( identifiers={(DOMAIN, self.hub_id, self.device_id)}, name=self.switch_name, manufacturer=MANUFACTURER, model=self.device_type, sw_version=sw_version, via_device=(DOMAIN, self.hub_id), )
同时 switch 也有版本号报错。 def device_info(self) -> DeviceInfo: """Return the device info.""" sw_version = self.raw_device_data.get("ver", "unknown") # 使用 get 方法提供默认版本号 return DeviceInfo( identifiers={(DOMAIN, self.hub_id, self.device_id)}, name=self.switch_name, manufacturer=MANUFACTURER, model=self.device_type, sw_version=sw_version, via_device=(DOMAIN, self.hub_id), )
额,这个还要单独处理版本号?他的接口好奇怪,我之前是直接封装完成的。
我用chatgpt解决的,这个报错不改程序会卡住,像插座,人体传感器都同步不过来。
最近运行了几周都没问题,唯一问题好像是API那边的问题,一两天同步状态会被卡住一次。 辛苦在下个版本更新中把这2个地方修正一下,以免下次我又下错了。谢谢
谢谢,如果有就完美了。