diff --git a/family/how-to/ha-automations.md b/family/how-to/ha-automations.md index 1e71b8d6..4f5cc6dd 100644 --- a/family/how-to/ha-automations.md +++ b/family/how-to/ha-automations.md @@ -2,9 +2,11 @@ > **Справочник логики автоматизаций** (`automations.yaml` на t610). Топология/команды/Modbus — [[family/how-to/home-automation]]. -> 🟢 **25 АВТОМАТИЗАЦИЙ — 24 `on`, 1 `off`** (`Ventilation automation on`, намеренно), `unavailable` = 0. Zigbee работает на ZHA. ✅ Проверено 2026-09-16 после починки кабинета. +> 🟢 **25 АВТОМАТИЗАЦИЙ — 24 `on`, 1 `off`** (`Ventilation automation on`, намеренно), `unavailable` = 0. Zigbee работает на ZHA. > -> **Состав:** 12 контроля батарей (§5) + 2 циркуляция ГВС + 2 свет кабинета (§2.2, §3, §3.1) + 2 подсветка лестницы + 2 ночной свет душевой + 2 диммер спальни + 1 протечка котельная + 1 вентиляция + **1 греющий кабель ввода воды (§7)**. +> **Состав:** 12 контроля батарей (§5) + 2 циркуляция ГВС + 2 свет кабинета (§2.2, §3) + 2 подсветка лестницы + 2 ночной свет душевой (§4.1) + 2 диммер спальни + 1 протечка котельная + 1 вентиляция + **1 греющий кабель ввода воды (§7)**. +> +> 🗓 **Сессия 2026-09-16 (день):** починены два дефекта автоматизаций кабинета — мёртвые `entity_id` в триггерах (§2.2) и потерянная защита `not_from` (§3); в душевой добавлена задержка 2 с на ветку по освещённости (§4.1). Все три правки верифицированы живым прогоном на железе. > > **Ключевые факты:** > - 🔴 **ЗАЩИТА ТРИГГЕРА ОБЯЗАТЕЛЬНА для `platform: state` на реле/кнопках:** `not_from: [unavailable, unknown]`. Без неё автоматизация срабатывает при старте HA (`unavailable → on`) и дёргает действие. Реальный случай 2026-09-16 — мигание света в кабинете. **При пересборке автоматизаций это поле теряется молча** (§3). @@ -109,7 +111,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` | **Вкл. ночной свет душевая** | присутствие + темно → `light.dushevaia_night_light` on | +| `1771997851260` | **Вкл. ночной свет душевая** | `occupied` (`id: presence` — мгновенно) / `illuminance below: 6` (`id: lux` → `delay 2 s` → проверка присутствия) → `light.dushevaia_night_light` on. См. §4.1 | | `1771997918348` | **Выкл. ночной свет душевая** | нет присутствия / светло → off | | `1773451257968` | Протечка котельная | `moist` → `notify.notify` | | `8800000000000`…`8800000000011` | **Батарея: 12 шт.** | см. §6 | @@ -257,7 +259,7 @@ trigger: **Направление фикса:** привязать триггер к конкретному состоянию (`to:`) ИЛИ заменить на триггер по событию нажатия. Требует определить фактическое поведение кнопки при физическом нажатии (сейчас кнопки в `on` — это состояние после старта, не нажатие). -**Статус:** ⚠️ **НЕ ИСПРАВЛЕНО.** Требует замера поведения кнопки при нажатии. +**Статус:** ✅ **ИСПРАВЛЕНО 2026-09-16** — фиксом `not_from: [unavailable, unknown]` (см. §3). Направление «привязать к конкретному состоянию через `to:`» оказалось **не обязательным**: наблюдаемый порядок при старте — `unavailable → on`, и его достаточно отсечь через `not_from`. `to:` не добавлялся сознательно — он бы сломал работу, потому что кнопка не всегда возвращается в одно и то же состояние. --- @@ -275,9 +277,54 @@ trigger: | | id | Триггер | Условие | |---|---|---|---| -| ВКЛ | `1771997851260` | `illuminance below: 6` + `occupied` | `below: 6` + `is_occupied` | +| ВКЛ | `1771997851260` | `occupied` (`id: presence`) + `illuminance below: 6` (`id: lux`) | `is_illuminance below 6` | | ВЫКЛ | `1771997918348` | `not_occupied` + `illuminance above: 6` | ⚠️ **`conditions: []`** — пусто | +### 4.1. ✅ Задержка 2 с на ветке освещённости (2026-09-16) + +**Задача (Alex):** «в триггере ночного света по изменению освещённости добавить задержку в пару секунд на проверку присутствия (освещённость упала → sleep 2 → присутствие == true → включить)». **Только на триггер по свету** — по присутствию включать мгновенно. + +**Зачем:** радар mmWave отдаёт `presence` с задержкой; при падении освещённости присутствие могло ещё не успеть выставиться → условие не проходило → свет не включался. + +**Реализация — `trigger.id` + `choose`:** + +```yaml +triggers: +- {type: occupied, entity_id: binary_sensor.shower_2_presence_sensor_presence, id: presence} +- {type: illuminance, entity_id: sensor.shower_2_presence_sensor_illuminance, below: 6, id: lux} +conditions: +- {condition: device, type: is_illuminance, ... below: 6} +actions: +- choose: + - conditions: [{condition: trigger, id: lux}] # ТОЛЬКО ветка по свету + sequence: + - {delay: {seconds: 2}} # ← задержка + - {condition: device, type: is_occupied, ...} # ← присутствие ПОСЛЕ задержки + default: [] +- {action: light.turn_on, target: {entity_id: light.dushevaia_night_light}} +mode: single +``` + +Распределение по триггерам: **`lux` → sleep 2 → проверка присутствия → включить; `presence` → сразу.** + +> 🔴 **ПИТФОЛЛ: `condition: state` с числовым порогом НЕ принимает `below`.** HA отбивает POST: `Message malformed: not a valid option at 'conditions[0].below'`. Порог по освещённости задаётся **только** device-условием `type: is_illuminance`. Ошибка приходит валидацией — конфиг не портится (проверено: после отказа значения остались прежними). +> 🔴 **ПИТФОЛЛ: `automation.trigger` НЕ подставляет `trigger.id`.** Прогон через него всегда уходит в `default` — ветку `choose` по `trigger.id` так **не протестировать**. Проверять воспроизведением логики временным скриптом (`/api/config/script/config/` → reload → вызов → DELETE). +> ⚠️ `delay` живёт **только в `actions:`** — он не знает, какой триггер сработал. Различать ветки обязательно через `trigger.id` + `choose`. + +**Верификация живым прогоном:** +| Проверка | Метод | Результат | +|---|---|---| +| `delay: 2` реально работает | временный скрипт + замер `last_changed` | вызов `06:13:34` → свет `06:13:37.24` ✅ | +| Ветка `lux` без присутствия **не** включает | скрипт с `is_occupied` при `presence=off` | свет остался `off` ✅ | +| Цепочка действий рабочая | `automation.trigger` + `skip_condition` | свет включился ✅ | +| Автоматизации целы | `/api/states` | 25 всего, 24 `on`, 1 `off`, `unavailable` = 0 ✅ | +| Временные сущности вычищены | поиск `zz_*` в states | 0 ✅ | + +**Бэкап:** `/config/automations.yaml.bak-showerdelay-20260916-131126` на t610, `~/tmp-t610/automations/automations.yaml.shower-before` локально. +**Скрипты:** `~/tmp-t610/fix_shower_lux_delay.sh` (правка), `test_shower_lux_branch.sh` (негативный тест), `test_delay_timing.sh` (замер задержки). + +> 📌 **ВЫКЛ (`1771997918348`) НЕ тронут** — по решению Alex задержка нужна только на триггере по свету. `conditions: []` у него остались. + **История правки (2026-09-15):** порог `8 → 6`. Порог `8` лежал ровно в центре дребезга датчика (**7 ↔ 12**), рабочий диапазон датчика всего **0…15 lx** → ВКЛ/ВЫКЛ хлопали по кругу. **⚠️ Остаточный риск (принят Alex):** при **2 lx** (покой) ВЫКЛ ждёт `above: 6` и может не наступить → свет залипнет. Гистерезис сознательно не сделан. diff --git a/family/tech/zigbee-t610-z2m-i-zha.md b/family/tech/zigbee-t610-z2m-i-zha.md index 4c4bfe4c..5983f3d0 100644 --- a/family/tech/zigbee-t610-z2m-i-zha.md +++ b/family/tech/zigbee-t610-z2m-i-zha.md @@ -408,6 +408,8 @@ curl -s -H "$HDR" http://supervisor/backups | jq | 28 | 🔴 **Отставшие автоматизации живут в СТАРОМ формате записи** | Две автоматизации кабинета остались на `triggers:`/`action:` (мн. ч.) + `- platform: state`, пока 23 перешли на `trigger:`/`action:` (ед. ч.) + `- trigger: state`. Обе формы работают, но **патчи по новому формату их не задевают** → не переехали на живые `entity_id` И потеряли `not_from`. **Один корень, два симптома.** Проверка: `grep -c '^- platform:' /config/automations.yaml` > 0 = есть отставшие. Править всё равно через REST-конфиг по `id`, не правкой файла | | 29 | ⚠️ **`alias` возвращается `null` после POST в `config/automation/config/`** | НЕ потеря alias — так отвечает API. Сущность `automation.` продолжает существовать, `triggers`/`action` применяются. **Не «восстанавливать» alias повторной записью** — только читать `triggers`/`action` для сверки | | 30 | 🔴 **BusyBox `date` на t610 не знает `-v-6H`** | BSD-синтаксис macOS. Рабочая форма: `date -u -d "@$(( $(date +%s) - 21600 ))" "+%Y-%m-%dT%H:%M:%S"`. Пустой ответ `history/period` за 48 ч — граница recorder, не поломка | +| 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/` → `script/reload` → вызов → `DELETE`. ⚠️ `delay` живёт только в `actions:` и не знает, какой триггер сработал — ветки различать через `trigger.id` + `choose` | ---