更好的「在家」状态

Home Assistant的Device_tracker是个非常有用的组件,通过设备追踪可以判断设备的在线情况从而执行不同的自动化操作。

可是iPhone的睡眠策略(长时间睡眠后断开WiFI)会导致设备追踪误以为设备已经离开从而执行「不在家」的自动化。

就像这样:

多次断连

大家的解决方案也是很多:

  • 追踪WIFI和蓝牙两者都断开则为「离开」
  • 通过家门口的人体传感器联动判断是否是「回家」
  • 通过人脸识别检测是否是「回家」

但是当「出去扔个垃圾再回家」后却仍然执行了「回家」自动化。有天看到Phil论坛有个网友提出了是否在家的5种状态,完美的解决了这个问题。

原理

这个组件采用了一个input_select来存储设备是否在家的5种状态:

状态 描述
JUST ARRIVED 刚刚到家
HOME 在家
JUST LEFT 刚刚离开
AWAY 离开
EXTENDED AWAY 长时间离开
input_select:
  hades_status_dropdown:
    options:
      - Home
      - Just Arrived
      - Just Left
      - Away
      - Extended Away
    initial: Home

然后通过一个Sensor组件来显示这个设备的状态:

sensor:
  - platform: template
    sensors:
      hades_status:
        value_template: "{{ states.input_select.hades_status_dropdown.state }}"
        friendly_name: 'hades status'

状态转换

因为原来的设备只有两种状态,而新的组件通过「刚刚到家」、「刚刚离开」这两种「过渡态」让系统可以判断出是否是临时断线或者是是否是第一次回家。

状态图

当回家时,状态过渡为「Just Arrived」,「Just Arrived」持续10秒钟后可以认为已经到家了,状态改为「Home」,可以执行「回家」自动化。

当手机睡眠断线或者出去拿外卖、扔垃圾时,状态过渡为「Just Away」,在一个时间之后(比如10分钟)仍然无法追踪到设备,此时系统可以理解设备是真的离开家了,所以状态过渡为「Away」,执行「离家」自动化。

当长时间(24小时)设备仍然离线,那我应该是去旅游去了哈哈哈哈,此时状态改为「Extended Away」。

自动化

但是这样有个缺点,需要执行5个自动化来判断所有的状态。

automation:
- id: '156766432451'
  alias: 标记为刚刚到家(Just Arrived)
  trigger:
  - entity_id: device_tracker.e4_9a_dc_bf_9a_de
    from: not_home
    platform: state
    to: home
  condition:
  - condition: state
    entity_id: sensor.hades_status
    state: Away
  action:
  - data:
      entity_id: input_select.hades_status_dropdown
      option: Just Arrived
    service: input_select.select_option
- id: '156764685703'
  alias: 标记为真正到家(Home)
  trigger:
  - entity_id: input_select.hades_status_dropdown
    for: 00:00:10
    platform: state
    to: Just Arrived
  - entity_id: input_select.hades_status_dropdown
    from: Just Left
    platform: state
    to: Just Arrived
  condition: []
  action:
  - data:
      entity_id: input_select.hades_status_dropdown
      option: Home
    service: input_select.select_option
- id: '156766457492'
  alias: 标记为刚刚离开(Just Left)
  trigger:
  - entity_id: device_tracker.e4_9a_dc_bf_9a_de
    from: home
    platform: state
    to: not_home
  condition: []
  action:
  - data:
      entity_id: input_select.hades_status_dropdown
      option: Just Left
    service: input_select.select_option
- id: '156766995990'
  alias: 标记为真正离开(Away)
  trigger:
  - entity_id: input_select.hades_status_dropdown
    for: 00:10:00
    platform: state
    to: Just Left
  condition: []
  action:
  - data:
      entity_id: input_select.hades_status_dropdown
      option: Away
    service: input_select.select_option
- id: '156766190138'
  alias: 标记为真正离开(Home ahadesistant Start)
  trigger:
  - event: start
    platform: homeahadesistant
  condition:
  - condition: state
    entity_id: device_tracker.e4_9a_dc_bf_9a_de
    state: not_home
  action:
  - data:
      entity_id: input_select.hades_status_dropdown
      option: Away
    service: input_select.select_option

其中,For为持续的时间,可以根据实际情况修改需要的时间。目前只能追踪单个设备,而更多的设备可以使用作者的模板,添加不同的设备来追踪。

这样子,只需要根据Sensor这个组件的状态是不是从「Just Arrived」到「Home」就能判断出是不是回家了。

Hub Center

Home Assistant提供了一套HADashboard,可以通过简洁的面板显示所有设备的状态:

HADashboard

有时候通过HomePod传达指令后就忘记了设备是否打开,所以一直想使用一块屏幕来显示家里的设备信息。但是需要当我「离开」的时候自动关闭屏幕。「在家」状态搞定后,可以更好的控制Hub Center了。

Hub Center的自动化应该这样设置:

  • 当我到家后,开启Hub Center
  • 当我离开后,关闭Hub Center
  • 当我睡觉时,执行场景关闭Hub Center

有了自动化,就应该考虑到使用什么来显示Hub Center了。

设备

iPad似乎是个不错的选择,但是不能远程/局域网内控制屏幕的开启与关闭,正好前几天拆解了一台笔记本,剩下了一个屏幕,接入一个驱动版就可以显示了。

但是某宝看了一圈,外漏驱动版似乎不太安全,定制外壳又很贵,最重要的是树莓派在房间的一个角落,如果连接树莓派的话需要8米的HDMI线,外壳+HDMI线成本就上去了。于是想到了被遗弃的安卓机,已经Root,通过ADB来控制屏幕的开启与关闭,简直完美。

ADB远程调试

通过USB连接电脑,adb devices查看设备,这种方法需要root权限:

/system/build.prop文件从手机pull出来:

adb shell pull /system/build.prop 

添加配置:

vim /system/build.prop 
+ service.adb.tcp.port=5555

最后在push到设备中:

adb push build.prop /system/

这样就可以通过远程连接到设备了。

模拟电源键

通过模拟电源按键来实现屏幕的开启与关闭:

adb connect 192.168.1.127:5555
adb shell input keyevent 26
adb disconnect

Hub Center状态

手机一直连接充电器是非常危险的,需要知道手机的剩余电量,在电量低的时候自动充电。所以需要一个组件来保存设备的状态信息,在Home Assistant里添加templatesensor

sensor:
  - platform: template
    sensors:
      hub_level:
        value_template: 0
        unit_of_measurement: '%'
        friendly_name: 'Hub Level'

  - platform: template
    sensors:
      hub_temperature:
        value_template: 0
        unit_of_measurement: '°C'
        friendly_name: 'Hub Temperature'

使用Python来推送设备的信息至Home Assistant:

import os
import re
import requests
import json

os.popen('adb connect 192.168.1.127:5555')
tmp = os.popen('adb shell dumpsys battery').readlines()
os.popen('adb disconnect')

level = re.findall('\d+',tmp[9])[0]
temperature = re.findall('\d+',tmp[13])[0]

headers = {
    "Content-Type": "application/json"
}

def set_state(id, state):
    requests.post('http://localhost/api/states/' + id, headers = headers, data = json.dumps({
        "state": state
    }), stream=True)

          
set_state('sensor.hub_level', level)
set_state('sensor.hub_temperature', int(temperature) / 10)

最后使用crontab任务定时更新这个组件的状态,每10分钟更新一下数据。这样,Home Assistant里就可以得到设备的电量和温度信息,在电量低于15%的时候打开插座就可以给手机充电了。

😺 温度的作用是来监控设备的充电状态,当充电时设备温度高时自动切断充电电源:

script:
  alias: 温度异常
  sequence:
  - data:
      message: 已关闭Hub充电插座电源
      title: Hub设备温度超过50℃
    service: notify.me
  - data:
      entity_id: switch.plug_hub
    service: switch.turn_off

控制屏幕

有了ADB工具就可以做许多事情了,使用Home Assistant的shell_command组件就可以控制设备屏幕的开启与关闭:

shell_command:
 hub_power: adb shell input keyevent 26

This integration can expose regular shell commands as services. Services can be called from a script or in automation. Shell commands aren’t allowed for a camel-case naming, please use lowercase naming only and separate the names with underscores.

通过这个组件所有的命令就可以接入到自动化中。

充电关闭屏幕

还有一个比较麻烦的事情就是,当我不在家时进行充电会激活屏幕显示,而Hub设置了永不锁屏,所以需要判断我是否在家从而关闭屏幕。

执行充电操作后,如果我不在家,通过adb shell dumpsys power来检测设备是否休眠,若不在休眠中(亮屏)则关闭屏幕:

#!/bin/bash
STATUS=$(adb shell dumpsys power | grep -i "Display Power")
if [[ "$STATUS" =~ "Display Power: state=ON" ]]
then
    adb shell input keyevent 26
fi

现在所有的设备已经全部接入到了Home Assistant了,通过构建不同的自动化操作就可以做到「回家开启Hub Center」、「出门关闭Hub Center」、「低电量自动充电」、「充电完成关闭插座」等自动化了。

emmm,手机屏幕还是有点小,要不从闲鱼收个安卓平板?