前不久逛瀚思彼岸帖子,被安利了Appdaemon。最近腾出时间痛着并快乐着看了几天官方的E文教程,然后想来实践下。于是翻出角落里N久前刷好了Tasmota固件然后吃灰的Sonoff、一时兴起买的光照度传感器和红外传感器,就决定做个简单的走廊灯自动控制吧。期间还是折腾了一番,感觉Tasmota还有Appdaemon还有太多的东西要学了,还是先写点东西作个总结吧。
本来只想简单附个Appdaemon代码算了,结果强迫症发作还是把整个流程都介绍了。所参考的教程都标注出来了,涉及的东西有点多,自行按需食用。
0.说明
1. 代码中”{[中文]}”的内容需要自行确认进行替换。
2. 因为实践时间跨度有点长,估计会有遗漏的地方,建议也观看参考教程。
PS:DEBUG、INFO、WARN代表提醒的重要程度。
1.环境
1.1.硬件(买买买)
- sonoff basic(淘宝28元左右那款)
- BH1750光照度传感器(6元左右)
- HC-SR501人体传感器(6元左右)
- CH340G USB转TTL模块(8元左右)
- 杜邦线7条
- 排针(2.54MM间距,17MM长,买长点容易掰弯走线)
- 电烙铁+焊锡+松香
1.2.软件环境
- Ubuntu 18.04(自行百度安装)
- mosquitto 1.4.15(安装参考教程)
- Homeassistant 0.71.0 (安装参考教程)
- Docker 18.03.1-ce(安装参考教程)
- Appdaemon 3.0.1
1.3.预期效果
光线暗并且感应到人,开灯
感应到没人,延时一段时间(自定义)后关灯
Tips
HC-SR501检测到人之后设置一个计时器,计时器结束后才输出低电平。期间检测到人活动,会重置计时器,所以关灯的时间是HC-SR501的计时器(可通过旋钮调)+自定义时间。
使用无线开关进行物理控制
2.Sonoff
Step1.刷固件
先把排针焊接在中间的5个孔上
建议
我买的两个模块焊盘都是堵上的,如果没有吸锡器(其实吸锡器没想象中好用),可以把排针拆成一个一个,加热锡盘时候把排针顶进去。
然后按教程刷好固件
一个坑
com参数的Flash Mode:DIO要改成DOUT!
Tips
按住开关接电才能进入刷机模式。
Step2.接线
光照传感器 <–> Sonoff
VCC - 3.3V
SCL - GPIO 3
SDA - GPIO 1
GND - GND
人体传感器 <–> Sonoff
VCC - 3.3V
OUT - GPIO 14
GND - GND
Tips
人体传感器一般供电是5V,可能是3.3V供电影响,距离旋钮不能调大,不然好像工作不正常,具体请自行测试。
两个传感器需要共用3.3V和GND供电,有美观强迫症的朋友可以把杜邦线的头抽出来(挑起条形的挡片就能抽出来了),然后把另一根线的头剪掉、剥线绕在前一根的金属头上,再放回去就好看了。
Step3.配置
- 设置时区
1
2#主页>Console
$ Timezone 8 - 设置switch2的模式
1
2#主页>Console
$ SwitchMode2 1Tips
默认工作模式是0,详细解释可见帖子
- 设置传感器上报间隔
1
2#主页>Configuration>Configure Logging
配置Telemetry period,最小是10,一般60就可以Tips
这个只影响照度的上传频率,因为代码可以通过http接口主动取数据,不用担心设置过大影响准确率
- 设置mqtt
1
2
3
4#主页>Configuration>Configure MQTT
按mosquitto的设置配置Host、Port、User、Password
#主页>Configuration>Configure Other
勾上"MQTT enable"选项 - 设置数据端口(参考教程)
1
2
3
4#主页>Configuration>Configure Module
#人体传感器数据
GPIO 4:Relay2。
GPIO 14:Switch2。Tips
原理应该是借助Relay的状态来间接传递人体传感器的输出(有人>高电平>Switch2接通>Relay2接通>MQTT传递状态)
1
2
3# 光照传感器数据
GPIO 3:I2C SCL
GPIO 1:I2C SDA
3.Homeassistant
{[HA配置目录]}/Configuration.yaml下,增加一个sensor,用于接收光照度信息;增加一个binary_sensor,用于接收触发状态;增加一个mqtt,与mosquitto主机通信;增加一个light/switch,Sonoff控制的灯。
1 | sensor: |
4.Appdaemon
4.1.安装
安装好docker后,直接运行一条命令即可。
1 | docker run --name=appdaemon -d -p 5050:5050 \ |
Tips
-v 是映射宿主的目录到docker实例中去,不用进docker实例去修改配置文件。
4.2.配置
{[appdaemon配置目录]}/appdaemon.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24secrets: /conf/secrets.yaml
log:
accessfile: /conf/logs/access.log
errorfile: /conf/logs/error.log
logfile: /conf/logs/appdaemon.log
log_generations: 3
log_size: 1000000
appdaemon:
threads: 10
time_zone: Asia/Shanghai
api_port: 5000
api_key: {[api访问密码,自定义]}
# api_ssl_certificate: <path/to/root/CA/cert>
# api_ssl_key: <path/to/root/CA/key>
plugins:
HASS:
type: hass
ha_url: {[ha的http地址]}
ha_key: {[ha的密码]}
# cert_path: <path/to/root/CA/cert>
# cert_verify: True
namespace: default
hadashboard:
dash_url: http://{[docker宿主ip]}:5050注意
Appdaemon 3.x版本相对2.x版本配置文件有了调整。
{[appdaemon配置目录]}/apps/motion_light.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34import appdaemon.plugins.hass.hassapi as hass
import requests
import datetime
class MotionLights(hass.Hass):
def initialize(self):
self._resource=self.args["resource"] #Sonoff的http接口
self._delay=self.args["delay"]
self._light=self.args["light_id"]
self._illuminance=self.args["illuminance_id"]
self._motion_sensor=self.args["motion_sensor_id"]
self.listen_state(self.motion, self._motion_sensor, new = "on")
self.listen_state(self.no_motion, self._motion_sensor, new = "off")
self._last_motion_time=datetime.datetime.now()
self._last_no_motion_time=datetime.datetime.now()
def motion(self, entity, attribute, old, new, kwargs):
self._last_motion_time=datetime.datetime.now()
res = requests.get(self._resource)
try:
illuminance=res.json()["StatusSNS"]["BH1750"]["Illuminance"] #通过Sonoff的http接口获取最新的照度数据
except:
illuminance=self.get_state(self._illuminance) #如果通过http获取失败,则取mqtt的数据
if illuminance < 20 and self.get_state(self._light) == "off": #亮度低于20并且灯处于关闭状态才执行
self.turn_on(self._light) #开灯
def no_motion(self, entity, attribute, old, new, kwargs):
self._last_no_motion_time=datetime.datetime.now()
self.run_in(self.light_off,self._delay) #无人,经过设定的延时时间后执行关灯
def light_off(self, kwargs):
if self._last_no_motion_time > self._last_motion_time and self.get_state(self._light) == "on": #无新触发情况(on->off后,在delay时间内发生off->on情况)并且灯处于开状态才执行
self.turn_off(self._light) #关灯注意
Appdaemon 3.x版本相对2.x版本import的模块发生了变化。
{appdaemon配置目录}/apps/apps.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14hallway_motion_light:
module: motion_light
class: MotionLights
resource: "http://{[Sonoff的IP]}/cm?cmnd=Status 10&user=admin&password=[Sonoff设置的密码]" #http查询接口
light_id: light.hallway #所控制的灯,HA配置
illuminance_id: sensor.illuminance_hallway #光照度传感器,HA配置
motion_sensor_id: binary_sensor.motion_hallway #移动传感器,HA配置
delay: 10 #无感应后延时关灯时间(s)
hallway_switch:
module: aqara_switch
class: Switchs
switch_id: binary_sensor.wall_switch_xxxxxxxxxxxx_2 #无线开关id,我用的是aqara墙壁开关
target_device: light.hallway #所控制的灯,HA配置
click_type: single #按键类型Tips
resource是Sonoff的http获取光照度的接口,如果Sonoff没有设置密码去掉"&user=admin&password=[Sonoff设置的密码]"即可。