找回密碼
 立即註冊
惟家LINE群QRCODE
    查看: 1|回覆: 0

    寫自動化之前,先畫一張狀態圖(五分鐘省下三小時)

    [複製鏈接]

    !lvup!   100%

    363

    主題

    27

    回帖

    2200萬

    積分

    管理員

    積分
    22005752
    發表於 3 天前 | 顯示全部樓層 |閱讀模式
    自動化寫到一定數量之後,會開始遇到一種很難查的問題:
    東西會自己動,但你不知道為什麼

    原因幾乎都一樣:你從來沒有想清楚「家」有哪些狀態



    症狀長什麼樣


    • 離家模式開著,但燈還是會被感應器打開
    • 睡覺時冷氣被「回家自動化」重新打開
    • 兩個自動化搶著控制同一盞燈,燈一直閃
    • 度假模式忘了關,回家後什麼都不會動


    這些都不是程式錯誤,是狀態衝突

    核心觀念:家在任何時刻只能處於一種主要狀態

    先把家的狀態列出來。我家是五個:


    • 在家-日間
    • 在家-夜間
    • 睡覺
    • 外出
    • 度假(超過一天不在)


    五個以內最好。超過八個的話,你大概是把「情境」(看電影、吃飯)
    跟「狀態」混在一起了 —— 那些應該是另一個維度。

    用 input_select 實作
    1. input_select:
    2.   house_mode:
    3.     name: 家的狀態
    4.     options:
    5.       - 在家-日間
    6.       - 在家-夜間
    7.       - 睡覺
    8.       - 外出
    9.       - 度假
    10.     initial: 在家-日間
    11.     icon: mdi:home-heart
    複製代碼

    接著畫轉換圖

    不用畫得漂亮,紙筆就好。重點是回答三個問題:


    • 從 A 可以到哪些狀態?
    • 什麼事件會造成這個轉換?
    • 轉換的時候要做什麼?


    我家的轉換規則:
    1. 在家-日間  --(日落後 30 分)-->  在家-夜間
    2. 在家-夜間  --(23:00 或按睡覺鈕)-->  睡覺
    3. 睡覺       --(06:00 或偵測到起床)-->  在家-日間
    4. 任何狀態   --(所有人離家 10 分鐘)-->  外出
    5. 外出       --(有人回家)-->  在家-日間 或 在家-夜間(看時間)
    6. 外出       --(超過 24 小時)-->  度假
    7. 度假       --(有人回家)-->  在家-日間
    複製代碼



    實作:一個自動化管所有轉換
    1. alias: 家的狀態機
    2. id: house_state_machine
    3. triggers:
    4.   - trigger: sun
    5.     event: sunset
    6.     offset: "00:30:00"
    7.     id: 入夜
    8.   - trigger: time
    9.     at: "23:00:00"
    10.     id: 該睡了
    11.   - trigger: state
    12.     entity_id: input_button.睡覺
    13.     id: 該睡了
    14.   - trigger: time
    15.     at: "06:00:00"
    16.     id: 起床
    17.   - trigger: state
    18.     entity_id: binary_sensor.家裡真的有人
    19.     to: "off"
    20.     for: "00:10:00"
    21.     id: 沒人了
    22.   - trigger: state
    23.     entity_id: binary_sensor.家裡真的有人
    24.     to: "on"
    25.     id: 有人回來
    26.   - trigger: state
    27.     entity_id: input_select.house_mode
    28.     to: 外出
    29.     for: "24:00:00"
    30.     id: 太久沒回來
    31. conditions: []
    32. actions:
    33.   - variables:
    34.       目前: "{{ states('input_select.house_mode') }}"
    35.       入夜了: >
    36.         {{ now() > (state_attr('sun.sun','next_setting') | as_datetime | as_local)
    37.            or now().hour >= 18 or now().hour < 6 }}
    38.   - choose:
    39.       # 入夜:只有「在家-日間」才會轉
    40.       - conditions:
    41.           - condition: trigger
    42.             id: 入夜
    43.           - "{{ 目前 == '在家-日間' }}"
    44.         sequence:
    45.           - action: input_select.select_option
    46.             target: {entity_id: input_select.house_mode}
    47.             data: {option: 在家-夜間}
    48.       # 睡覺:只有在家的兩個狀態才會轉
    49.       - conditions:
    50.           - condition: trigger
    51.             id: 該睡了
    52.           - "{{ 目前 in ['在家-日間', '在家-夜間'] }}"
    53.         sequence:
    54.           - action: input_select.select_option
    55.             target: {entity_id: input_select.house_mode}
    56.             data: {option: 睡覺}
    57.       # 起床
    58.       - conditions:
    59.           - condition: trigger
    60.             id: 起床
    61.           - "{{ 目前 == '睡覺' }}"
    62.         sequence:
    63.           - action: input_select.select_option
    64.             target: {entity_id: input_select.house_mode}
    65.             data: {option: 在家-日間}
    66.       # 沒人了 → 外出(任何狀態都可以轉)
    67.       - conditions:
    68.           - condition: trigger
    69.             id: 沒人了
    70.           - "{{ 目前 != '度假' }}"
    71.         sequence:
    72.           - action: input_select.select_option
    73.             target: {entity_id: input_select.house_mode}
    74.             data: {option: 外出}
    75.       # 有人回來
    76.       - conditions:
    77.           - condition: trigger
    78.             id: 有人回來
    79.           - "{{ 目前 in ['外出', '度假'] }}"
    80.         sequence:
    81.           - action: input_select.select_option
    82.             target: {entity_id: input_select.house_mode}
    83.             data: {option: "{{ '在家-夜間' if 入夜了 else '在家-日間' }}"}
    84.       # 太久沒回來 → 度假
    85.       - conditions:
    86.           - condition: trigger
    87.             id: 太久沒回來
    88.         sequence:
    89.           - action: input_select.select_option
    90.             target: {entity_id: input_select.house_mode}
    91.             data: {option: 度假}
    92. mode: queued
    93. max: 5
    複製代碼

    關鍵在那些 "{{ 目前 == ... }}" 條件。

    它們確保只有合法的轉換會發生
    睡覺中不會因為「入夜」又跑回「在家-夜間」。

    然後:所有其他自動化都只看狀態

    這是整套設計的重點。
    1. # 感應燈
    2. alias: 走道感應燈
    3. triggers:
    4.   - trigger: state
    5.     entity_id: binary_sensor.走道感應
    6.     to: "on"
    7. conditions:
    8.   - condition: not
    9.     conditions:
    10.       - condition: state
    11.         entity_id: input_select.house_mode
    12.         state: 度假
    13. actions:
    14.   - action: light.turn_on
    15.     target: {entity_id: light.走道}
    16.     data:
    17.       brightness_pct: >
    18.         {% set m = states('input_select.house_mode') %}
    19.         {{ 5 if m == '睡覺' else (30 if m == '在家-夜間' else 80) }}
    20. mode: restart
    複製代碼

    一個實體、一個判斷,所有自動化行為一致。

    狀態轉換時要做的事,另外寫
    1. alias: 狀態轉換時的動作
    2. triggers:
    3.   - trigger: state
    4.     entity_id: input_select.house_mode
    5. actions:
    6.   - choose:
    7.       - conditions: "{{ trigger.to_state.state == '外出' }}"
    8.         sequence:
    9.           - action: scene.turn_on
    10.             target: {entity_id: scene.離家}
    11.           - action: notify.mobile_app_charles
    12.             data: {message: 已切換到外出模式}
    13.       - conditions: "{{ trigger.to_state.state == '睡覺' }}"
    14.         sequence:
    15.           - action: script.turn_on
    16.             target: {entity_id: script.睡前模式}
    17.       - conditions: "{{ trigger.to_state.state == '度假' }}"
    18.         sequence:
    19.           - action: script.turn_on
    20.             target: {entity_id: script.度假模式}
    21. mode: queued
    複製代碼

    把「狀態怎麼轉」和「轉了之後做什麼」分開
    兩個都會變得好讀很多。

    怎麼檢查有沒有漏洞

    畫完轉換圖之後,逐一問自己:


    • 每個狀態都有「進來」的路徑嗎?(沒有的話那個狀態永遠到不了)
    • 每個狀態都有「出去」的路徑嗎?(沒有的話會卡死)
    • 有沒有兩條路徑會同時觸發?(會打架)
    • 斷電重開之後,狀態會是什麼?


    最後一個最常被忘記。

    input_select 的 initial 只在第一次建立時生效,
    之後 HA 會記住上次的值。所以重啟後可能停在「睡覺」但現在是下午。

    我的解法是加一個啟動檢查:
    1. alias: 重啟後校正狀態
    2. triggers:
    3.   - trigger: homeassistant
    4.     event: start
    5. actions:
    6.   - delay: "00:02:00"      # 等感測器都回報了
    7.   - action: input_select.select_option
    8.     target: {entity_id: input_select.house_mode}
    9.     data:
    10.       option: >
    11.         {% if not is_state('binary_sensor.家裡真的有人','on') %}外出
    12.         {% elif now().hour >= 23 or now().hour < 6 %}睡覺
    13.         {% elif now().hour >= 18 %}在家-夜間
    14.         {% else %}在家-日間{% endif %}
    15. mode: single
    複製代碼

    ---

    我是在家裡的自動化超過二十個之後才被迫做這件事的。
    早點做會省很多重寫的時間。

    如果你現在只有五六個自動化,也建議先把狀態列出來 ——
    就算暫時不實作,之後寫每個自動化的時候心裡會有一張圖。
    您需要登入後纔可以回帖 登入 | 立即註冊

    本版積分規則

    Archiver|手機版|惟家的智能論壇

    GMT+8, 2026-9-24 11:32 , Processed in 0.077157 second(s), 24 queries .

    快速回覆 返回頂部 返回列表