快來試一試吧
- 背景介紹
-
- 引言
- 運作環境
- 如何擷取硬體資訊
- Command line Sensor(重點)
-
- CPU 溫度示例(★)
-
- 注意事項
- 監控所有的失敗登入
- 從遠端檔案擷取數值
- 解析 JSON 資料(★)
-
- 注意事項
- 階段成果
- Template Sensor(重點)
-
- 配置
- 階段成果
- 基本完工
-
- 分組
- 未來工作
- 通過 ESPHome 在 OLED 上顯示
- 結語
背景介紹
引言
我們知道在 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 直接将載荷資料 輸出、列印出來 即可,即類似于上面的
指令;或者 Python 的cat
函數,可以看到在上一節擷取硬體資訊的腳本中,最後是一行列印函數print
。至于這裡為什麼要用print(json.dumps(RaspiInfo))
我們後面再介紹。json.dumps()
- 若使用 value_template 提取資料,則在模闆表達式中使用
代替指令輸出的載荷資料,而變量value
則可以代替經 JSON 格式解析後的資料,具體的使用請參考文檔 Processing incoming data。value_json
下面再通過幾個例子感受一下上面說的内容:
監控所有的失敗登入
# 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 格式的編碼。最後列印出的資料示例如:json.dumps()
{"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 中看到的
目錄下的内容,和 Hassio 配置檔案的預設路徑config
下的内容是一樣的。/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 上顯示
待填坑
結語
不知不覺寫了好長時間将近九千個字(符),太長不看系列。希望自己把問題描述的足夠清楚了,在這裡也隻是抛磚引玉,有了這一遍流程,大家就已經可以自己動手開發自己想要的各類“傳感器”了,别忘了來分享喔。