更好的「在家」状态

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

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

就像这样:

多次断连

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

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

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

原理

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

状态描述
JUST ARRIVED刚刚到家
HOME在家
JUST LEFT刚刚离开
AWAY离开
EXTENDED AWAY长时间离开
1
2
3
4
5
6
7
8
9
input_select:
hades_status_dropdown:
options:
- Home
- Just Arrived
- Just Left
- Away
- Extended Away
initial: Home

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

1
2
3
4
5
6
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个自动化来判断所有的状态。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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出来:

1
adb shell pull /system/build.prop

添加配置:

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

最后在push到设备中:

1
adb push build.prop /system/

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

模拟电源键

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

1
2
3
adb connect 192.168.1.127:5555
adb shell input keyevent 26
adb disconnect

Hub Center状态

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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%的时候打开插座就可以给手机充电了。

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

1
2
3
4
5
6
7
8
9
10
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组件就可以控制设备屏幕的开启与关闭:

1
2
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来检测设备是否休眠,若不在休眠中(亮屏)则关闭屏幕:

1
2
3
4
5
6
#!/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,手机屏幕还是有点小,要不从闲鱼收个安卓平板?

🥳 加载Disqus评论