背景介绍
引言
我们知道在 Home Assistant 中,sensor
平台的作用就是获取各种数据,但是有时候我们想要的设备或特殊的数据,HA 并没有指定的platform
支持,因此 HA 给我们提供了一个强大的 Command line Sensor(命令行传感器)平台。它可以通过指定的命令获取数据,也就意味着几乎可以接入 HA 任何种类的(包括具体的和抽象的)传感器。 下面本文就带你一步步手撸一个自己的传感器出来~
运行环境
树莓派 3B+ 在 Docker 下安装 Hassio,使用 Samba
add-on,在 VS Code 中使用 Home Assistant Config Helper
插件编辑 YAML 文件。
如何获取硬件信息
这里以 epsilon1 的帖子为参考,修改了一部分以适应 HA 的要求,运行环境为 Python3。不同的平台(这里是树莓派),可能相应的需要做出一些修改。
- 注意: 在 Docker 内、外运行相同的命令,如获取 CPU 和内存使用情况,会得到不同的结果(没有学习过 Docker,直觉认为在 Docker 内外还是有差异的),因此以下脚本中注释了用户空间占用 CPU 百分比的信息。
import os
import json
# Return CPU temperature as a float
def getCPUtemperature():
f = os.popen("cat /sys/class/thermal/thermal_zone0/temp")
temp = int(f.readline().strip())/1000
return round(temp, 1)
# Return RAM information (unit=MB) in a list
# Index 0: total RAM
# Index 1: used RAM
# Index 2: free RAM
def getRAMinfo():
f = os.popen("free | awk '/Mem/ {print $2,$3,$4}'")
info = f.readline().split()
info = [round(int(i)/1024, 1) for i in info]
return info
'''
# Return % of CPU used by user as float
def getCPUinfo():
# Docker外部 info = os.popen("top -n1 | awk '/Cpu\(s\):/ {print $2}'").readline().strip()
# Docker内部 info = os.popen("top -n1 | awk '/CPU:/ {print $2}'").readline().strip()
if info:
return float(info)
else:
# 未获取到信息,返回默认错误值
return -1.0
'''
# Return information about disk space as a list (unit included)
# Index 0: total disk space
# Index 1: used disk space
# Index 2: remaining disk space
# Index 3: percentage of disk used
def getDiskinfo():
f = os.popen("df -h /")
info = f.readlines()[1].split()[1:5]
return info
if __name__ == '__main__':
RaspiInfo = {}
RaspiInfo['CPUtemp'] = getCPUtemperature()
RaspiInfo['RAMinfo'] = getRAMinfo()
RaspiInfo['DISKinfo'] = getDiskinfo()
#RaspiInfo['CPUuse'] = getCPUinfo()
# 必须转化为标准 JSON 格式备用,下文有解释
print(json.dumps(RaspiInfo))
运行打印出的数据示例:{"CPUtemp": 44.0, "RAMinfo": [874.5, 340.4, 37.7], "DISKinfo": ["15G", "8.5G", "5.3G", "62%"]}
Command line Sensor(重点)
废话不多说,下面就来看看这个命令行传感器如何配置吧!完整配置信息请参考官方文档 Command line Sensor ,下面摘录一部分作主要说明。
CPU 温度示例(★)
许多关于硬件的信息可以由 proc
文件系统获得,这里展示获取 CPU 温度的方法。
# configuration.yaml 示例,仅用于学习配置命令行传感器,并不是本次项目的配置文件
# 在 sensor 域下添加
sensor:
# 平台名,不用多说
- platform: command_line
# 添加传感器实体 entity 的名称,引用时为 sensor.cpu_temperature,可在 开发者工具-状态 内看到
name: CPU Temperature
# 用于获取温度数据的命令,注意单、双引号的使用
command: "cat /sys/class/thermal/thermal_zone0/temp"
#(可选)传感器数据的单位
unit_of_measurement: "°C"
#(可选)定义一个模板,从 command 传回的载荷数据(用 value 代替)中提取需要的值,
# 若未定义模板,则直接将 command 传回的数据作为传感器的值。
value_template: '{{ value | multiply(0.001) | round(1) }}'
#(可选)更新间隔,默认为 60s
scan_interval: 60
#(可选)命令执行超时设置,默认为 15s
command_timeout: 15
注意事项
- 获取数据的命令 command 直接将载荷数据 输出、打印出来 即可,即类似于上面的
cat
命令;或者 Python 的 print
函数,可以看到在上一节获取硬件信息的脚本中,最后是一行打印函数 print(json.dumps(RaspiInfo))
。至于这里为什么要用 json.dumps()
我们后面再介绍。
- 若使用 value_template 提取数据,则在模板表达式中使用
value
代替命令输出的载荷数据,而变量 value_json
则可以代替经 JSON 格式解析后的数据,具体的使用请参考文档 Processing incoming data。
下面再通过几个例子感受一下上面说的内容:
监控所有的失败登录
# configuration.yaml entry 示例
sensor:
- platform: command_line
name: badlogin
command: "grep -c 'Login attempt' /home/hass/.homeassistant/home-assistant.log"
从远程文件获取数值
使用 Python 的 requests 库,通过 HTTP 获取指定 URL 的内容作为传感器的数值。
可以学习一下这里的 python3 -c "xxxxx"
通过命令行方式执行 Python 脚本的方式。
sensor:
- platform: command_line
command: python3 -c "import requests; print(requests.get('http://remote-host/sensor_data.txt').text)"
name: File value
同样可以执行外部的脚本,如下:
sensor:
- platform: command_line
name: Brightness
command: "python3 /path/to/script/arest-value.py"
解析 JSON 数据(★)
好了,当你大致熟悉了这个命令行传感器怎么配置后,我们再来介绍它剩下的一个强大的功能:(可选)json_attributes
。在这个配置选项下,我们定义一系列字典的键(Key),这样命令行传感器就能根据这些键名,从命令传回的 JSON 字典数据中提取键对应的值,并将其设置为传感器的属性。这样一来,我们就能一次性传回多个数值了,非常方便。
- 注意:传回的字典一定要是符合标准 JSON 格式的,所以在上面的脚本中,最后一行利用了 Python 库函数
json.dumps()
对字典进行 JSON 格式的编码。最后打印出的数据示例如:
{"CPUtemp": 44.0, "RAMinfo": [874.5, 340.4, 37.7], "DISKinfo": ["15G", "8.5G", "5.3G", "62%"]}
根据以上字典内容,我们在配置文件的 sensor
域下添加:
# 我是在 configuration.yaml 中添加了一行 sensor: !include sensor.yaml
# 所以以下是 sensor.yaml 文件的内容
- platform: command_line
name: RaspInfo
scan_interval: 60
command: "python3 /config/scripts/queryRaspi.py" # 脚本路径问题参考下面注意事项
json_attributes: # 键名可为大小写
- RAMinfo
- DISKinfo
- CPUuse
- CPUtemp
注意事项
- 当执行外部脚本时,脚本的存放路径显得格外重要。如果是在 Docker 下安装的 Hassio,脚本路径应该写为
command: "python3 /config/scripts/queryRaspi.py"
,而不能用绝对路径 command: python3 "/usr/share/hassio/homeassistant/scripts/queryRaspi.py"
,会出现找不到文件或目录的错误。
- 而其他方式安装的 HA,我没有验证过,配置中应该就是写脚本的绝对路径,至少官方文档中示例写的就是绝对路径(如
command: 'python3 /home/pi/.homeassistant/scripts/datetime.py'
),大家试一下会不会报错吧。
- 事实上,在我这种安装环境下(Docker + Hassio),在 Samba 中看到的
config
目录下的内容,和 Hassio 配置文件的默认路径 /usr/share/hassio/homeassistant
下的内容是一样的。
- 因此,现在就把上面查询树莓派各种信息的 Python 脚本保存到
config
目录下(也就是 /usr/share/hassio/homeassistant/scripts/queryRaspi.py
)
阶段成果
至此,重新启动 Home Assistant 服务后(你也可以在后面所有配置完成后再重启),我们将在 开发者工具-状态 里看到我们定义的一个命令行传感器实体。
- 可以看到实体名称就是我们定义的
RaspInfo
的小写 raspinfo
;相应的,若取名为 Rasp Info
,实体名称就会变为 rasp_info
- 状态(States)栏已经获得了脚本命令正确的返回值,并且属性(Attributes)栏里已经成功将字典里的键(Key)解析为这个实体的属性。
以上都没有问题的话,我们继续往下看。
Template Sensor(重点)
虽然上面的命令行传感器已经能够将(一整项)状态数据显示在主页了,但是该怎么把各项数据单独作为传感器实体显示出来呢?事实上 template
平台支持我们定义一个从其它实体获取值的传感器,也就是 Template Sensor
,而这正是我们想要的功能(利用命令行传感器的各个属性值)。
下面就来看看这个基于模板的传感器如何配置吧!完整配置信息请参考官方文档 Template Sensor 。
- 注:这一部分主要涉及的是模板(template)的使用,这里不作赘述,可以参考文档 Templating
配置
在配置文件的 sensor
域下添加:
# 我是在 configuration.yaml 中添加了一行 sensor: !include sensor.yaml
# 所以以下是 sensor.yaml 文件的内容
# 平台名称
- platform: template
# 传感器列表
sensors:
# 实体名称:小写,下划线
cpu_temp:
# (可选)在前端显示的传感器昵称
friendly_name: "CPU Temperature"
# (可选)传感器数值的单位
unit_of_measurement: '℃'
#(必须)定义一个获取传感器状态(数值)的模板
# 这里就是获取上面定义的命令行传感器实体 sensor.raspinfo 的相应属性值,注意大小写
value_template: "{{state_attr('sensor.raspinfo', 'CPUtemp')}}"
# 以下配置类似,不再赘述
ram_total:
friendly_name: "RAM total"
unit_of_measurement: 'MB'
value_template: "{{state_attr('sensor.raspinfo', 'RAMinfo')[0]}}"
ram_used:
friendly_name: "RAM used"
unit_of_measurement: 'MB'
value_template: "{{state_attr('sensor.raspinfo', 'RAMinfo')[1]}}"
ram_free:
friendly_name: "RAM free"
unit_of_measurement: 'MB'
value_template: "{{state_attr('sensor.raspinfo', 'RAMinfo')[2]}}"
disk_total:
friendly_name: "DISK total"
value_template: "{{state_attr('sensor.raspinfo', 'DISKinfo')[0]}}"
disk_used:
friendly_name: "DISK used"
value_template: "{{state_attr('sensor.raspinfo', 'DISKinfo')[1]}}"
disk_left:
friendly_name: "DISK left"
value_template: "{{state_attr('sensor.raspinfo', 'DISKinfo')[2]}}"
disk_percentage:
friendly_name: "DISK percentage"
value_template: "{{state_attr('sensor.raspinfo', 'DISKinfo')[3]}}"
注意:这里配置的比较精简(足够用了,实在是太累了,不想再打字了,而且官方文档也很清晰),完整配置信息请参考官方文档 Template Sensor ,要学会模板(template)的使用。
阶段成果
至此,重新启动 Home Assistant 服务后(你也可以在后面所有配置完成后再重启),我们将在 开发者工具-状态 里看到所有刚刚创建的传感器的单独实体。
- 若在前端主页出现实体不可用的警告或者玄学问题,可以尝试重新启动 Home Assistant 服务并注意观察日志信息
基本完工
分组
最后只需要简单的分组即可:
# 我是在 configuration.yaml 中添加了一行 group: !include groups.yaml
# 所以以下是 groups.yaml 文件的内容
default_view:
view: yes
icon: mdi:home
entities:
- group.raspinfo
raspinfo:
name: 树莓派
control: hidden
entities:
- sensor.cpu_temp
- sensor.ram_total
- sensor.ram_used
- sensor.ram_free
- sensor.disk_total
- sensor.disk_used
- sensor.disk_left
- sensor.disk_percentage
至此,重新启动 Home Assistant 服务后(若前面已经重启过,这里只需要重载分组即可),在前端面板便出现了各项传感器信息:
Bingo !
未来工作
- 完整阅读文档,了解所有配置(有个印象,方便查阅)
- 对着文档,修改传感器图标使得更加美观
- .. .. ..
通过 ESPHome 在 OLED 上显示
待填坑
战术总结
- 一定要学会查看日志,根据报错信息对症下药
- 不知不觉写了好长时间将近九千个字(符),太长不看系列。希望自己把问题描述的足够清楚了,在这里也只是抛砖引玉,有了这一遍流程,大家就已经可以自己动手开发自己想要的各类“传感器”了,别忘了来分享喔。对了,新人求回复。文章还发在 CSDN。