本帖最后由 cnk700i 于 2019-5-28 10:21 编辑
PS:本贴只有原因分析,改版插件下载移步至https://bbs.hassbian.com/thread-5200-1-1.html
Update:2018.08.15 更正一下Throttle其实是不带有定期更新功能的,只是限制调用频率。。。总结两种情况下会造成复位:
1、执行一次开关操作5s内执行另外一次开关操作。
2、HA的周期更新(会调用update())后5s内,刚好执行开关操作,也会造成复位。
另外:
1、switch默认SCAN_INTERVAL是30s。
2、插件作者设置TIME_BETWEEN_UPDATES的目的是为了减少通信请求:因为每次周期更新,每个插孔都会执行4次与排通信。
Update:2018.07.06 重新研究了C.S大的代码,发现之前的理解有误,C.S大是每次开、关也是立刻取排插的状态的,应该不存在排插反馈状态慢的问题,之前分析有误。于是又再研读代码,确认是由于BroadlinkMP1Switchl类的update方法用了Throttle原因导致:这个东西的作用是只规定时间内执行一次update并且只能执行一次,所以导致HA在开/关无法获取最新的状态。
所以解决办法就是把@Throttle(TIME_BETWEEN_UPDATES)删掉即可,这也是之前修改的代码有效的原因。。。官方组件更新时间是TIME_BETWEEN_UPDATES=5s,实际上HA已经有周期性的更新功能,配置文件通过scan_interval设置更新时间即可。
Update:2018.06.15 参考yeelight灯的代码,摸索增加了可用状态的判断。
趁着618活动入了中草已久的鸿雁插排IHC8340B玩玩,4个独立分控,wifi插线板比wifi插座划算多了。看论坛以前的帖子,用官方的broadlink MP1插件即可。到手之后用broadlink的易控APP设置好网络,按官网的配置指引配置好后,发现控制是可以控制了,不过有点诡异,点开关图标经常回复先前的状态,控制倒是正常。比如开关是“关”状态,点开之后插线板接通了,但HA的开关状态立刻切回“关”状态,等一会后状态就切回为“开”了。于是又是一番研究代码折腾,算是找到初步解决方法了。
一、测试环境
configuration.yaml
switch:
- platform: broadlink
scan_interval: 15 #自动更新的间隔,默认30s
host: 设备ip地址
mac: '设备mac地址'
type: mp1
slots:
slot_1: 'slot1' #HA会实例化一个switch.slot1设备,这里不要用中文,否则HA实例化的设备名称是switch.x(x是数字),有一定随机性
slot_2: 'slot2'
slot_3: 'slot3'
slot_4: 'slot4'
二、原因分析
处理的流程如下(个人理解,不一定准确)
1.web页面点击开关按钮,触发turn_on或turn_off service
2.通信、控制
3.设置开关状态并调用schedule_update_ha_state通告状态变更
4.通信查询状态并更新开关状态
#ha安装目录/homeassistant/components/switch/__init__.py
#SwitchDevice类async def async_setup(hass, config)函数 部分代码
async def async_handle_switch_service(service):
"""Handle calls to the switch services."""
target_switches = component.async_extract_from_service(service)
update_tasks = []
for switch in target_switches:
if service.service == SERVICE_TURN_ON:
await switch.async_turn_on() #步骤1
elif service.service == SERVICE_TOGGLE:
await switch.async_toggle()
else:
await switch.async_turn_off()
if not switch.should_poll:
continue
update_tasks.append(switch.async_update_ha_state(True)) #步骤4,后续会调用broadlink.py中update方法
if update_tasks:
await asyncio.wait(update_tasks, loop=hass.loop)
#ha安装目录/homeassistant/components/switch/broadlink.py
#BroadlinkRMSwitch类 部分代码
def turn_on(self, **kwargs):
"""Turn the device on."""
if self._sendpacket(self._command_on): #步骤2
self._state = True #步骤3
self.schedule_update_ha_state() #步骤3
def turn_off(self, **kwargs): #流程同turn_on
"""Turn the device off."""
if self._sendpacket(self._command_off):
self._state = False
self.schedule_update_ha_state()
结论:由于步骤3、步骤4执行时间基本一致,导致步骤4去向插线板获取状态时,插线板返回的状态有一定的滞后性,导致开关状态复原,要等待一个scan_interval更新周期状态才会重新同步。
三、解决方案
步骤4查询插线板开关状态需要调用broadlink.py中BroadlinkMP1Slot类的update方法,所以在turn_on或者turn_off记录下点击开关的时间,然后在update方法中判断时间如果不超过3s则不执行update。
副作用就是点击操作后,如果3s内碰上定期更新周期,定期更新就不会执行了,不过影响不大,总比状态跳来跳去好。
|