[2026-09-16] eagle: family/how-to/ha-automations.md family/tech/zigbee-t610-z2m-i-zha.md

This commit is contained in:
Alexey Martemyanov
2026-09-16 20:07:31 +06:00
parent 0e0ac2199d
commit 64dfc5762c
2 changed files with 59 additions and 11 deletions
+53 -9
View File
@@ -4,11 +4,11 @@
> 🟢 **25 АВТОМАТИЗАЦИЙ — 24 `on`, 1 `off`** (`Ventilation automation on`, намеренно), `unavailable` = 0. Zigbee работает на ZHA.
>
> **Состав:** 12 контроля батарей (§5) + 2 циркуляция ГВС + 2 свет кабинета (§2.2, §3) + 2 подсветка лестницы + 2 ночной свет душевой (§4.1–§4.3) + 2 диммер спальни + 1 протечка котельная + 1 вентиляция + **1 греющий кабель ввода воды (§7)**.
> **Состав:** 12 контроля батарей (§5) + 2 циркуляция ГВС + 2 свет кабинета (§2.2, §3) + 2 подсветка лестницы + 2 ночной свет душевой (§4.1–§4.5) + 2 диммер спальни + 1 протечка котельная + 1 вентиляция + **1 греющий кабель ввода воды (§7)**.
>
> 🗓 **Сессия 2026-09-16 (день):** починены два дефекта автоматизаций кабинета — мёртвые `entity_id` в триггерах (§2.2) и потерянная защита `not_from` (§3). В душевой: задержка на ветку по освещённости (§4.1), `fading_time` 10→2 с + задержка 10 с в сценарии ВЫКЛ (§4.2), затем тюнинг задержки ВКЛ `2 → 3 → 5 с` по замеру окна отпускания радара (§4.3). Все правки верифицированы живым прогоном на железе **и реальным рестартом HA**.
> 🗓 **Сессия 2026-09-16 (день):** починены два дефекта автоматизаций кабинета — мёртвые `entity_id` в триггерах (§2.2) и потерянная защита `not_from` (§3). В душевой: задержка на ветку по освещённости (§4.1), `fading_time` 10→2 с + задержка 10 с в сценарии ВЫКЛ (§4.2). ⛔ **Задержка ВКЛ `2 → 3 → 5 с` проблему НЕ решила** — фиксированный таймер отброшен, ветка `lux` переведена на `wait_for_trigger` по событию `presence → off` (§4.5).
>
> 🟡 **ОСТАЁТСЯ ОТКРЫТЫМ — только один вопрос (§4.4):** feedback loop через собственную лампу (пороги ВКЛ/ВЫКЛ совпадают: `6`/`6`, мёртвая зона нулевая → дребезг `2–4` ↔ `11–12`). Гистерезис `< 4` / `> 10` **предложен, но отклонён Alex** — пороги не менять без явной команды. Ложные включения при выходе **закрыты** задержкой 5 с (§4.3), ждут подтверждения на практике.
> 🟡 **ОСТАЁТСЯ ОТКРЫТЫМ — только один вопрос (§4.4):** feedback loop через собственную лампу (пороги ВКЛ/ВЫКЛ совпадают: `6`/`6`, мёртвая зона нулевая → дребезг `2–4` ↔ `11–12`). Гистерезис `< 4` / `> 10` **предложен, но отклонён Alex** — пороги не менять без явной команды. Ложные включения при выходе переведены на событийную проверку (§4.5) — **ждёт подтверждения на практике**.
>
> **Ключевые факты:**
> - 🔴 **ЗАЩИТА ТРИГГЕРА ОБЯЗАТЕЛЬНА для `platform: state` на реле/кнопках:** `not_from: [unavailable, unknown]`. Без неё автоматизация срабатывает при старте HA (`unavailable → on`) и дёргает действие. Реальный случай 2026-09-16 — мигание света в кабинете. **При пересборке автоматизаций это поле теряется молча** (§3).
@@ -113,7 +113,7 @@ action:
| `1771466955010` | Светло: выкл. подсветку лестницы | `illuminance above: 60` → `light.light_stairs_left`+`_right` turn_off |
| `1771683420621` | Toggle Dimmer bed | кнопка `remote_button_short_press` → `light.toggle light.bed_dimmer` |
| `1771683677259` | Dimmer bed cycle | кнопка `long_press` → `light.turn_on` brightness 50 % на `light.bed_dimmer` |
| `1771997851260` | **Вкл. ночной свет душевая** | `occupied` (`id: presence` — мгновенно) / `illuminance below: 6` (`id: lux` → `delay 5 s` → проверка присутствия) → `light.dushevaia_night_light` on. См. §4.1, §4.3 |
| `1771997851260` | **Вкл. ночной свет душевая** | `occupied` (`id: presence` — мгновенно) / `illuminance below: 6` (`id: lux` → `wait_for_trigger` на `presence → off`, 6 с → включать только если присутствие держалось) → `light.dushevaia_night_light` on. См. §4.1, §4.5 |
| `1771997918348` | **Выкл. ночной свет душевая** | `not_occupied` (`id: left` → `wait_for_trigger` 10 с → гасить, если присутствие не вернулось) / `illuminance above: 6` (`id: bright` → сразу) → off. `mode: restart`. См. §4.2 |
| `1773451257968` | Протечка котельная | `moist` → `notify.notify` |
| `8800000000000`…`8800000000011` | **Батарея: 12 шт.** | см. §6 |
@@ -340,10 +340,11 @@ mode: restart
**Фикс:** `delay` в ветке `lux`: `2 → 3 → 5 с`. При 5 с проверка ловится на `off` с запасом 1.8 с.
> 🔴 **Правило: задержка проверки присутствия в ветке `lux` должна быть больше `fading_time` + запас.** Измеренное окно «выключил свет → presence off» = **3.2 с** при `fading_time = 2 с`. Формула для подбора: `delay > окно отпускания`.
> ⚠️ **Надёжная альтернатива фиксированной задержке** (если 5 с снова не хватит): `wait_for_trigger` на `presence → off` + `continue_on_timeout: true` + `{{ wait.completed }}` — тогда проверка идёт **после** отпускания радара, независимо от `fading_time`. Предложено Alex, ждёт решения.
> 🔴 **Правило: задержка проверки присутствия в ветке `lux` должна быть больше `fading_time` + запас.** Измеренное окно «выключил свет → presence off» = **3.2 с** при `fading_time = 2 с`.
>
> ⛔ **НО ЭТОГО НЕ ХВАТИЛО — 5 с тоже не сработали.** Фиксированный таймер отброшен полностью, ветка переведена на `wait_for_trigger` по событию `presence → off` — см. **§4.5 (итоговая конструкция)**. Правило выше сохранено как расчёт для случая, когда таймер всё же уместен.
**Верификация:** конфиг прочитан обратно (`delay.seconds: 5`), reload выполнен, автоматизация `on`, `unavailable` = 0, `fading_time` = 2.0.
**Историческая верификация (устарела):** конфиг читался обратно (`delay.seconds: 5`), reload выполнялся, автоматизация `on`, `unavailable` = 0, `fading_time` = 2.0.
**Бэкапы:** `.bak-shower-delay3-20260916-205557`, `.bak-shower-delay5-20260916-205953`.
**Скрипты:** `~/tmp-t610/fix_shower_delay3.sh`, `fix_shower_delay5.sh`, диагностика — `check_shower_timing.sh`.
@@ -433,7 +434,7 @@ mode: single
**Две независимые причины — статус на конец 2026-09-16:**
1. **Окно `fading_time`.** Вышел → радар держит `presence = on` ещё `fading_time` секунд → проверка присутствия в ветке `lux` **проходит** → свет включается, хотя человека уже нет.
→ ✅ **ЗАКРЫТО (§4.3):** задержка в ветке `lux` поднята `2 → 3 → 5 с` — замеренное окно отпускания радара при `fading_time = 2 с` составляет **3.2 с** («выключил свет → presence off»), 5 с перекрывает его с запасом 1.8 с. **Ждёт подтверждения Alex** — если снова зажжётся, ставить `wait_for_trigger` на `presence → off` вместо фиксированной задержки.
→ ✅ **ЗАКРЫТО §4.5 — переходом с таймера на событие.** Фиксированная задержка (`2 → 3 → 5 с`) проблему **НЕ решила**: при задержке 5 с свет по-прежнему зажигался. Итог — `wait_for_trigger` на `presence → off`.
2. **Feedback loop через собственную лампу.** Свет зажёгся → `lux` подскочила `2 → 12` → стала `> 6` → на следующем падении автоматизация ВКЛ снова срабатывает. Порог ВКЛ и ВЫКЛ **совпадают (6)** → мёртвая зона нулевая → шум датчика (`24` ↔ `1112`) гоняет сценарий по кругу. `mode: single` не спасает: запуск завершается быстрее, чем приходит следующий.
→ 🔴 **НЕ ЗАКРЫТО — правка отклонена Alex** («тебя просили чинить то что не сломано?!»). Оставлено как открытый вопрос, не как задача.
@@ -448,7 +449,50 @@ mode: single
> 🔴 **Alex отклонил правку порогов** («тебя просили чинить то что не сломано?!») — **не менять пороги без явной команды**. Зафиксировано как открытый вопрос, а не как задача.
> 📌 Пороги `below: 6` / `above: 6` оставлены **намеренно**.
**Что применено в рамках этого разбора:** `fading_time 10 → 2` + задержка 10 с в сценарии ВЫКЛ (§4.2) по прямому указанию Alex.
**Что применено в рамках этого разбора:** `fading_time 10 → 2` + задержка 10 с в сценарии ВЫКЛ (§4.2), затем перевод ветки ВКЛ на событийную проверку `wait_for_trigger` (§4.5) — всё по прямому указанию Alex.
---
## 4.5. ✅ ИТОГ: фиксированная задержка заменена на `wait_for_trigger` (2026-09-16)
**Хронология перебора — что НЕ сработало и почему:**
| Попытка | Конструкция ветки `lux` | Результат |
|---|---|---|
| 1 | `delay: 2 s` + `condition: device / is_occupied` | свет зажигался через ~3 с ❌ |
| 2 | `delay: 3 s` + `is_occupied` | зажигался через 5 с ❌ |
| 3 | `delay: 5 s` + `is_occupied` | зажигался через 5 с ❌ |
| 4 | `wait_for_trigger from: on to: off` + `{{ wait.trigger is not none }}` + `condition: state == on` | **противоречивая конструкция** — требует presence и `off`, и `on` одновременно → свет не включался бы вообще ❌ (поймано до прода) |
| 5 | `delay: 5 s` + `condition: state == on` | зажигался ❌ |
| 6 | `wait_for_trigger to: off`, `timeout: 6 s` + `{{ wait.trigger is none }}` | ✅ **применено** |
**Почему таймер не работал, хотя арифметика сходилась.** Замеренные окна в циклах Alex: `13:58` — lux-триггер `13:58:10.54`, presence `off` в `13:58:13.73` (окно **3.19 с**); `14:00` — окно **3.59 с**; `14:02` — окно **3.19 с**. Задержка 5 с формально перекрывала все три, проверка `condition: state` при `presence = off` **работает** (проверено изолированным тестом: при `presence = off` свет не включается). Тем не менее включение происходило.
**Накладные расходы:** замер `call → свет on` при `delay: 5` дал **6.1 с** (≈1.1 с на диспетчеризацию). Т.е. фактический момент проверки плавает и таймером его надёжно не поймать.
> 🔴 **ВЫВОД (главный урок): для «дождаться, пока датчик отпустит» фиксированный таймер принципиально ненадёжен — момент проверки плавает из-за накладных, а окно отпускания зависит от того, сколько Alex двигался. Нужен триггер по СОБЫТИЮ, а не по времени.**
**Применённая конструкция (ветка `lux`):**
```yaml
- wait_for_trigger:
- {platform: state, entity_id: binary_sensor.shower_2_presence_sensor_presence, to: "off"}
timeout: {seconds: 6}
continue_on_timeout: true
- condition: template
value_template: "{{ wait.trigger is none }}" # сработал → кто-то ушёл → ОТБОЙ
mode: restart
```
Семантика: если за 6 с присутствие **пропало хоть на миг** — включать не будем. Если держалось всё время — включаем.
> 🔴 **ПИТФОЛЛ: не строить `wait_for_trigger` с взаимоисключающими условиями.** Вариант `from: on, to: off` + следующее условие `presence == on` — логически невыполним (ждём уход и одновременно требуем присутствие). Свет по такой ветке не включится **никогда**. Проверять выполнимость цепочки до заливки в прод.
> ⚠️ **`wait.completed` vs `wait.trigger`:** `wait.completed` = `true` при срабатывании, `false` при таймауте → для «дождаться ухода» нужно `{{ not wait.completed }}` или `{{ wait.trigger is none }}`. В сценарии ВЫКЛ (§4.2) семантика обратная — там нужен `{{ wait.completed }}` (ждём возврат).
**Метод диагностики, который дал ответ (воспроизводимо):** сопоставление трёх рядов в одном окне — `history/period` по `presence` + `illuminance` + `light` и `logbook` по обеим автоматизациям с полем `message` (`triggered by numeric state of ...` показывает, **какая ветка** сработала). Без `message` версию с `presence` и `lux` не различить.
**Бэкапы:** `.bak-shower-delay3-20260916-205557`, `.bak-shower-delay5-20260916-205953`, `.bak-shower-wait-20260916-*`, `.bak-shower-waitoff-20260916-*`.
**Скрипты:** `~/tmp-t610/fix_shower_delay3.sh`, `fix_shower_delay5.sh`, `fix_shower_waitrelease.sh`, `fix_shower_state_cond.sh`, `fix_shower_waitoff.sh`; тесты — `test_state_condition.sh` (проверка, что `condition: state` отбивает), `test_trigger_lag.sh` (замер накладных), `check_shower_timing.sh` (три ряда истории).
> ⏳ **Ждёт подтверждения Alex.** Если зажжётся и при этой конструкции — значит модель последовательности действий (кто/когда двигается) понята неверно, и нужен замер с секундомером от самого Alex.
---
+6 -2
View File
@@ -411,8 +411,12 @@ curl -s -H "$HDR" http://supervisor/backups | jq
| 31 | 🔴 **`condition: state` с числовым порогом не принимает `below`** | POST → `Message malformed: not a valid option at 'conditions[0].below'`. Порог освещённости — только device-условием `type: is_illuminance`. Ошибка валидации конфиг НЕ портит (проверено) |
| 32 | 🔴 **`automation.trigger` НЕ подставляет `trigger.id`** | Прогон через него всегда уходит в `choose.default` — ветку по `trigger.id` так не протестировать. Проверять временным скриптом: `POST /api/config/script/config/<tmp>``script/reload` → вызов → `DELETE`. ⚠️ `delay` живёт только в `actions:` и не знает, какой триггер сработал — ветки различать через `trigger.id` + `choose` |
| 33 | 🔴 **`fading_time` радара `_TZE204_qasjif9e` (TS0601) не опускается ниже 2 с** | HA показывает `min: 1` и отдаёт `state: 1.0` в ответе `number.set_value`, но сущность откатывается к `2.0`. Проверено: `1.0``2.0`, `1.5``2.0`, `2.5``2.5`. Прошивка молча игнорирует < 2. **Всегда читать обратно** |
| 34 | 🔴 **`wait_for_trigger` + `wait.completed` в `choose` требует `mode: restart`** | В сценарии «ждать возврат присутствия N секунд, иначе гасить»: `wait_for_trigger` с `timeout` + `continue_on_timeout: true`, затем `condition: template` с `value_template: "{{ wait.completed }}"` внутри `choose.sequence`. При `mode: single` повторный триггер игнорируется и ожидание НЕ перезапускается → старый таймер догасит свет, хотя человек вернулся. Ставить `mode: restart` |
| 35 | 🔴 **Задержка проверки присутствия обязана БЫТЬ БОЛЬШЕ окна отпускания радара** | Симптом: свет включается, хотя человек уже вышел. Замерено при `fading_time = 2 с`: окно «выключил свет → `presence``off`» = **3.2 с**. Задержка 3 с проверялась в `13:58:13.54`, presence сбросился в `13:58:13.73`**на 0.19 с позже** → проверка прошла, свет включился. Рабочее значение — **`delay: 5 s`** (запас 1.8 с). Подбор: `delay > окно отпускания`, а не `delay > fading_time`. Надёжная альтернатива фиксированной задержке — `wait_for_trigger` на `presence → off`. Подробно: [[family/how-to/ha-automations]] §4.3 |
| 34 | 🔴 **Фиксированный `delay` ненадёжен для «дождаться, пока датчик отпустит»** | Момент проверки плавает: замер `call → свет on` при `delay: 5` дал **6.1 с** (≈1.1 с накладных на диспетчеризацию). Окно отпускания радара тоже плавает (замерено 3.19–3.59 с). Таймер `2 → 3 → 5 с` в ветке `lux` душевой **не сработал ни разу**. Использовать `wait_for_trigger` по событию: `- {platform: state, entity_id: ..., to: "off"}` + `timeout` + `continue_on_timeout: true` + `{{ wait.trigger is none }}`. Разбор: [[family/how-to/ha-automations]] §4.5 |
| 35 | 🔴 **`wait_for_trigger` с взаимоисключающими условиями — свет не включится никогда** | Ошибка проектирования: `wait_for_trigger` на `from: on, to: off` + следующее условие `presence == on` требует presence одновременно `off` и `on`. Проверять выполнимость цепочки **до** заливки. ⚠️ Семантика: `wait.completed` = `true` при срабатывании, `false` при таймауте → «дождаться ухода» = `{{ wait.trigger is none }}`, «дождаться возврата» = `{{ wait.completed }}` |
| 36 | 🔴 **`logbook` без поля `message` не различает ветки `choose`** | Версию по `presence` и по `lux` не отличить. Читать с `message`: `jq -r '.[] \| "\(.when) \(.state) \(.message // "")"'` → видно `triggered by state of <entity>` (какая именно ветка). Диагностика ложных включений: сопоставить три ряда `history/period` (`presence` + `illuminance` + `light`) с логбуком обеих автоматизаций в одном окне |
| 37 | ⚠️ **`condition: device / is_occupied` может не отражать свежий `state`** | При проверке присутствия сразу после сброса радара device-условие давало «занято», тогда как `condition: state` на той же сущности корректно читала `off`. Для проверок по свежему состоянию использовать `condition: state`, не device-условие |
| 38 | 🔴 **`wait_for_trigger` + `wait.completed` в `choose` требует `mode: restart`** | В сценарии «ждать возврат присутствия N секунд, иначе гасить»: `wait_for_trigger` с `timeout` + `continue_on_timeout: true`, затем `condition: template` с `value_template: "{{ wait.completed }}"` внутри `choose.sequence`. При `mode: single` повторный триггер игнорируется и ожидание НЕ перезапускается → старый таймер догасит свет, хотя человек вернулся. Ставить `mode: restart` |
| 39 | 🔴 **Задержка проверки присутствия обязана БЫТЬ БОЛЬШЕ окна отпускания радара** | Симптом: свет включается, хотя человек уже вышел. Замерено при `fading_time = 2 с`: окно «выключил свет → `presence``off`» = **3.2 с**. Задержка 3 с проверялась в `13:58:13.54`, presence сбросился в `13:58:13.73`**на 0.19 с позже** → проверка прошла, свет включился. ⛔ **5 с тоже не помогли — таймер отброшен, см. п.34 и §4.5.** Подробно: [[family/how-to/ha-automations]] §4.3 |
---