基于Appdaemon+Home Assistant的Sonoff basic开关刷Tasmota搭载光照度+红外传感器实现自动化控灯

前不久逛瀚思彼岸帖子,被安利了Appdaemon。最近腾出时间痛着并快乐着看了几天官方的E文教程,然后想来实践下。于是翻出角落里N久前刷好了Tasmota固件然后吃灰的Sonoff、一时兴起买的光照度传感器和红外传感器,就决定做个简单的走廊灯自动控制吧。期间还是折腾了一番,感觉Tasmota还有Appdaemon还有太多的东西要学了,还是先写点东西作个总结吧。

本来只想简单附个Appdaemon代码算了,结果强迫症发作还是把整个流程都介绍了。所参考的教程都标注出来了,涉及的东西有点多,自行按需食用。


0.说明

INFO:代码中”{[中文]}”的内容需要自行确认进行替换。

DEBUG:因为实践时间跨度有点长,估计会有遗漏的地方,建议也观看参考教程。

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.软件环境

1.3.预期效果

  • 光线暗并且感应到人,开灯
  • 感应到没人,延时一段时间(自定义)后关灯

    DEBUG:HC-SR501检测到人之后设置一个计时器,计时器结束后才输出低电平。期间检测到人活动,会重置计时器,所以关灯的时间是HC-SR501的计时器(可通过旋钮调)+自定义时间。

  • 使用无线开关进行物理控制


2.Sonoff

Step1.刷固件

先把排针焊接在中间的5个孔上

DEBUG:我买的两个模块焊盘都是堵上的,如果没有吸锡器(其实吸锡器没想象中好用),可以把排针拆成一个一个,加热锡盘时候把排针顶进去。

然后按教程刷好固件

WARN:com参数的Flash Mode:DIO要改成DOUT!
INFO:按住开关接电才能进入刷机模式。

Step2.接线

光照传感器 <--> Sonoff
VCC - 3.3V    
SCL - GPIO 3    
SDA - GPIO 1    
GND - GND

人体传感器 <--> Sonoff
VCC - 3.3V    
OUT - GPIO 14  
GND - GND

INFO:人体传感器一般供电是5V,可能是3.3V供电影响,距离旋钮不能调大,不然好像工作不正常,具体请自行测试。

DEBUG:两个传感器需要共用3.3V和GND供电,有美观强迫症的朋友可以把杜邦线的头抽出来(挑起条形的挡片就能抽出来了),然后把另一根线的头剪掉、剥线绕在前一根的金属头上,再放回去就好看了。

Step3.配置

  • 设置时区

    1
    2
    #主页>Console
    $ Timezone 8
  • 设置switch2的模式

    1
    2
    #主页>Console
    $ SwitchMode2 1

    DEBUG:默认工作模式是0,详细解释可见帖子

  • 设置传感器上报间隔

    1
    2
    #主页>Configuration>Configure Logging
    配置Telemetry period,最小是10,一般60就可以

    DEBUG:这个只影响照度的上传频率,因为代码可以通过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。

    DEBUG:原理应该是借助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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
sensor:
  - platform: mqtt
    name: illuminance_hallway
    state_topic: "tele/sonoff/SENSOR"
    value_template: "{{ value_json['BH1750'].Illuminance }}"
    unit_of_measurement: "lux"

binary_sensor:
  - platform: mqtt
    name: motion_hallway
    qos: 1
    state_topic: "stat/sonoff/POWER2"
    payload_on: "ON"
    payload_off: "OFF"
    device_class: motion

mqtt:
  # MQTT Broker的IP地址或者域名
  broker: {[mosquitto主机的ip]}
  # MQTT Broker的端口号,缺省为1883
  port: 1883
  # 用户名
  username: {[自定义]}
  # 密码
  password: {[自定义]}


4.Appdaemon

4.1.安装

安装好docker后,直接运行一条命令即可。

1
2
3
4
5
docker run --name=appdaemon -d -p 5050:5050 \
--restart=always \
-v {[你的配置目录]}:/conf \
-v /etc/localtime:/etc/localtime \
/appdaemon:latest

INFO:-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
    24
    secrets: /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

    DEBUG: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
    34
    import 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)  #关灯

    DEBUG:3.x版本相对2.x版本import的模块发生了变化。

  • {appdaemon配置目录}/apps/apps.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    hallway_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                                            #按键类型

    DEBUG:resource是Sonoff的http获取光照度的接口,如果Sonoff没有设置密码去掉”&user=admin&password=[Sonoff设置的密码]”即可。