趁着618活动入了中草已久的鸿雁插排IHC8340B玩玩,4个独立分控,wifi插线板比wifi插座划算多了。看论坛以前的帖子,用官方的broadlink MP1插件即可。到手之后用broadlink的易控APP设置好网络,按官网的配置指引配置好后,发现控制是可以控制了,不过有点诡异,点开关图标经常回复先前的状态,控制倒是正常。比如开关是“关”状态,点开之后插线板接通了,但HA的开关状态立刻切回“关”状态,等一会后状态就切回为“开”了。于是又是一番研究代码折腾,算是找到初步解决方法了。
看了调试信息没发现什么异常,而且通过app学习、使用红外码就正常。孤寂了一段时间,意外在domoticz论坛一个[帖子][1]发现了[抓包分析方法][2],于是继续分析,算是找到了原因和临时解决办法。
1.准备
- IHC8340B插线板,固件版本v10028
- Ubuntu 18.04 + HA 0.70.1,
- HA的configuration.yaml配置
1
2
3
4
5
6
7
8
9
10
11switch:
- 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'
2.过程
2.1.工作流程分析
1)web页面点击开关按钮,触发turn_on或turn_off service(前端js)
2)进行通信、发送控制指令(插件turn_on方法)
3)设置开关状态并调用schedule_update_ha_state通告状态变更(插件turn_on方法)
4)service调用完毕后触发通信查询状态并更新开关状态(系统entity组件)
1 | #ha安装目录/homeassistant/components/switch/__init__.py |
1 | #ha安装目录/homeassistant/components/switch/broadlink.py |
Tips
async_update_ha_state(True)参数带true,会调用update方法(会与设备通信)去更新state。
HA某个版本(0.80.0左右吧)后,entity的service处理方法统一改用EntityComponent.async_register_entity_service()注册了,相关的service处理代码改到了helpers.service.py的_handle_service_platform_call()方法。不过总体处理逻辑没变,调用完entity相对应的service操作代码后,会根据entity的should_poll属性调用一次entity.async_update_ha_state(True)更新状态。
2.2.解决方法
去掉BroadlinkMP1Switch类中的@Throttle(TIME_BETWEEN_UPDATES)即可。
1 | class BroadlinkMP1Switch(object): |
INFO:Throttle的存在,限制update()更新频率为5s,造成2种情况下开关复位:1、执行一次开关操作5s内执行另外一次开关操作。2、HA的周期更新(会调用update())后5s内,刚好执行开关操作。
INFO:去掉Throttle副作用就是一个周期内,每个slot更新都会调用1次update(),建议适当调大scan_interval周期。
3.小结
- 之前错误分析问题原因:由于步骤3、步骤4执行时间基本一致,导致步骤4去向插线板获取状态时,插线板返回的状态有一定的滞后性,HA获取仍然是旧状态,最后导致开关状态复原,要等待一个scan_interval更新周期状态才会重新同步。还是要对插座单片机的处理性能有信心。
- 一个插座有4个slot,组件会将每一个slot实例化一个switch。当switch更新状态会调用父设备(即BroadlinkMP1Switch)的update()方法,插件作者设置TIME_BETWEEN_UPDATES就是为了限制不必要的通信。
- HA有周期性的更新platform里的entities功能,配置文件通过scan_interval设置更新时间。scan_interval默认是15s(entity_component.py中定义DEFAULT_SCAN_INTERVAL),switch类默认是30s(switch/__init__.py中定义)