|
|
玩了一段時間之後,我發現真正的分水嶺不是「會不會寫自動化」,
而是「半年後你還敢不敢動它」。
這篇講三個讓系統能長期活下去的原則。

原則一:每個判斷只寫一次
這是最重要的一條。
壞的樣子:
- # 自動化 A
- conditions:
- - "{{ now().hour >= 23 or now().hour < 6 }}"
- # 自動化 B
- conditions:
- - "{{ now().hour >= 23 or now().hour < 6 }}"
- # 自動化 C(這裡不小心寫成 22)
- conditions:
- - "{{ now().hour >= 22 or now().hour < 6 }}"
複製代碼
同一個「夜間」的定義散在三個地方,而且已經不一致了。
你不會發現,因為只差一小時。
好的樣子:
- template:
- - binary_sensor:
- - name: 夜間模式
- unique_id: night_mode
- state: >
- {{ now().hour >= 23 or now().hour < 6 }}
複製代碼
三個自動化都改成:
- conditions:
- - condition: state
- entity_id: binary_sensor.夜間模式
- state: "on"
複製代碼
三個額外的好處:
- 改定義只要改一個地方
- 可以在介面上直接看「現在是不是夜間」,除錯超快
- 可以拉到儀表板上,家人也知道現在是什麼模式
這條原則的一般形式:任何出現第二次的判斷,就該抽出來變成實體。

原則二:名字要能自我說明
半年後你看到這個:
- - action: script.turn_on
- target:
- entity_id: script.1690234871234
複製代碼
完全不知道它在幹嘛。
命名的三個層次:
- 實體 ID —— 英文、結構化。light.bedroom_ceiling
- 顯示名稱 —— 中文、人話。主臥天花板燈
- 自動化 alias —— 要能看出「什麼時候會發生什麼事」
自動化命名的壞例子和好例子:
- # 壞
- alias: 燈光自動化 2
- alias: 感應器
- alias: test
- # 好
- alias: 浴室有人時開燈,離開三分鐘關
- alias: 離家後五分鐘提醒門沒鎖
- alias: 濕度超過門檻自動抽風
複製代碼
好的 alias 讀起來就是一句完整的規則。
這樣自動化列表本身就是一份文件。
還有一件事:給自動化 id
- automation:
- - alias: 浴室感應燈
- id: bathroom_motion_light
複製代碼
沒有 id 的自動化不能在 UI 編輯、不能被追蹤、
而且重新排序後 entity_id 會跑掉(變成 automation.unnamed_2)。
原則三:讓系統能告訴你它壞了
這條最容易被忽略,但影響最大。
智能家居最可怕的不是壞掉,是「安靜地壞掉」。
感應器沒電了、Zigbee 裝置掉線了、某個整合壞了 ——
這些都不會報錯,只是那個自動化再也不會觸發。
你可能三個月後才發現。
做法一:不可用實體的每日巡檢
- template:
- - trigger:
- - trigger: time
- at: "09:00:00"
- sensor:
- - name: 不可用的實體
- unique_id: unavailable_entities
- state: >
- {{ states
- | selectattr('state', 'in', ['unavailable', 'unknown'])
- | rejectattr('entity_id', 'search', '^(device_tracker|person)\\.')
- | list | count }}
- attributes:
- 清單: >
- {{ states
- | selectattr('state', 'in', ['unavailable', 'unknown'])
- | rejectattr('entity_id', 'search', '^(device_tracker|person)\\.')
- | map(attribute='entity_id') | list }}
複製代碼
配一個自動化:數量 > 0 就推播。
做法二:電池低電量提醒
- template:
- - sensor:
- - name: 低電量裝置數
- unique_id: low_battery_count
- state: >
- {{ states.sensor
- | selectattr('attributes.device_class', 'defined')
- | selectattr('attributes.device_class', 'eq', 'battery')
- | map(attribute='state') | map('int', 100)
- | select('lt', 20) | list | count }}
複製代碼
做法三:「太久沒動」偵測
這個最有用。一個平常每天觸發的感應器,三天沒動就是有問題:
- template:
- - trigger:
- - trigger: time_pattern
- hours: "/6"
- binary_sensor:
- - name: 有裝置失聯
- unique_id: stale_devices
- state: >
- {{ states.binary_sensor
- | selectattr('attributes.device_class', 'defined')
- | selectattr('attributes.device_class', 'eq', 'motion')
- | selectattr('last_changed', 'lt', now() - timedelta(days=3))
- | list | count > 0 }}
- attributes:
- 失聯清單: >
- {{ states.binary_sensor
- | selectattr('attributes.device_class', 'defined')
- | selectattr('attributes.device_class', 'eq', 'motion')
- | selectattr('last_changed', 'lt', now() - timedelta(days=3))
- | map(attribute='name') | list }}
複製代碼
做法四:自動化健康檢查
每個月提醒你去看一次:
- alias: 每月自動化健康檢查
- triggers:
- - trigger: time
- at: "10:00:00"
- conditions:
- - "{{ now().day == 1 }}"
- actions:
- - action: notify.mobile_app_charles
- data:
- title: 每月檢查
- message: >
- 停用中的自動化:
- {{ states.automation | selectattr('state','eq','off')
- | map(attribute='name') | list | join('、') }}
- 三十天沒觸發過的:
- {{ states.automation
- | selectattr('attributes.last_triggered', 'defined')
- | selectattr('attributes.last_triggered', 'lt',
- now() - timedelta(days=30))
- | map(attribute='name') | list | join('、') }}
- mode: single
複製代碼
「三十天沒觸發過」這個清單特別有價值。
上面的東西不是壞了,就是根本不需要 —— 兩種都該處理。

一個額外的建議:定期刪東西
我每季會做一次「砍掉不用的」:
- 三十天沒觸發的自動化 → 看是壞了還是沒用,沒用就刪
- 儀表板上從來沒點過的卡片 → 刪
- 買來玩過一次就放著的裝置 → 從 HA 移除(不然它永遠是 unavailable)
- 測試用的輔助元件 → 刪
刪東西比加東西難,但對可維護性的幫助更大。
系統越小,你越清楚它在做什麼;越清楚,你越敢改。
---
回頭看,這三個原則其實可以濃縮成一句:
「讓半年後的自己,能在十分鐘內搞懂並安全地改動任何一部分。」
做得到的話,這套系統就能一直長大。做不到的話,
它會慢慢變成一個你不敢碰的黑盒子,最後整個重做。
(我重做過一次,所以知道那有多痛。) |
|