Files
obsidian-vault/family/how-to/ha-automations.md
T

27 KiB
Raw Blame History

⚙️ HA — автоматизации

Справочник логики автоматизаций (automations.yaml на t610). Топология/команды/Modbus — family/how-to/home-automation.

🟢 25 АВТОМАТИЗАЦИЙ — 24 on, 1 off (Ventilation automation on, намеренно), unavailable = 0. Zigbee работает на ZHA.

Состав: 12 контроля батарей (§5) + 2 циркуляция ГВС + 2 свет кабинета + 2 подсветка лестницы + 2 ночной свет душевой + 2 диммер спальни + 1 протечка котельная + 1 вентиляция + 1 греющий кабель ввода воды (§7).

Ключевые факты:

  • 🔴 entity_id автоматизаций HA перегенерирует по alias — искать по attributes.id, не по entity_id.
  • 🔴 REST GET/POST /api/config/automation/config/<id> использует поля во множественном числеtriggers/conditions/actions (в файле — единственное). Ошибка молча уходит в пустоту: POST → 200 ok, значения НЕ меняются. Всегда читать обратно.
  • ⚠️ Кнопка спальни шлёт remote_button_short_press только после zha/devices/reconfigure.

Бэкапы перед правками: automations.yaml.bak-cable3-20260915-* (кабель), .bak-preids-*, .bak-gard-* (единообразие ID). Локальные копии в ~/tmp-t610/. 📄 Рецепт пересборки после Z2M→ZHA и питфоллы — family/tech/zigbee-t610-z2m-i-zha.

🔴 ПИТФОЛЛЫ, найденные при починке:

  1. device-триггер батареи — тип battery_level, НЕ battery. Иначе Automation ... failed to setup triggers and has been disabled.
  2. binary_sensor «battery_low» в ZHA нет — только numeric sensor.*_battery, порог через below: 10.
  3. Домен entity_id не меняется переименованием: switch.office_table_light_switch_l1 → ZHA-имя light.tz3000_5gey1ohx_ts0002_osveshchenie. Автоматизации переписаны на light.*. 🍳 Сменить домен у СУЩЕСТВУЮЩЕЙ сущности HA нельзя (ни реестром, ни customize). Обход — Template-хелпер поверх исходной сущности. switch_as_x для этого не годится: принимает только switch, не light. Применено на вытяжке кухни: fan.kitchen_hood. Разбор — family/tech/kitchen-hood-fan-template.
  4. reloadPOST /api/services/automation/reload с long-lived JWT, порт 80 (http://172.30.32.1/api/). Супервизорский токен → 401.

1. Где живёт

  • Файл: /config/automations.yaml в HA Core на t610.
  • Всего: 25 автоматизаций — 24 on, 1 off (Ventilation automation on, намеренно), unavailable = 0. Файл = 780 строк.
  • Бэкапы перед правками: automations.yaml.bak-cable3-<ts> (кабель, 2026-09-16), .bak-preids-*, .bak-gard-*; локально — ~/tmp-t610/bak-cable-20260915-234837/automations.yaml.
  • Удалены 4 «призрака» (записи в реестре без тела): svetlo_vykl_osveshchenie_lestnitsy, temno_vkl_podsvetku_lestnitsy, datchik_osveshchennosti_lestnitsa_batareia, light_switch_bed_batareia. Тела не было (config/automation/config/<id> → 404) — лестничные сценарии пересозданы заново с теми же id.
  • Локальная копия: ~/tmp-t610/automations/automations.yaml; после пересборки — ~/tmp-t610/automations-new.yaml, ~/tmp-t610/automations-fixed.yaml.
  • Читать живьём через API (не по памяти):
    B="https://mallexxx.duckdns.org"
    curl -s -H @/tmp/h1 "$B/api/config/automation/config/<ID>"
    

🔴 ПИТФОЛЛ (стоил пустой заливки)

REST GET/POST /api/config/automation/config/<id> использует поля во множественном числе — triggers / conditions / actions (в файле automations.yamltrigger/condition/action). Правка по единственному числу молча уходит в пустые пути: POST → 200 {"result":"ok"}, значения НЕ меняются. → ВСЕГДА читать конфиг обратно и сверять фактические значения.

Бэкап перед правкой: cp ~/tmp-t610/automations/automations.yaml{,.bak-$(date +%Y%m%d-%H%M%S)}


2. Карта автоматизаций

id alias Логика
1768585827761 Выключить циркуляцию ГВС time 23:00 → turn_off
1768585922970 Включить циркуляцию ГВС time 09:30 → turn_on
1770404069135 Ventilation automation on time 05:00fan.turn_on fan.automation (off)
5735cb9f855e462dbdbdf680d0d6a66f office_pass_switch_table кнопка L1 → toggle light.smart_light_office_left ⚠️
45b96f6f38f6488ba70266fa5da665f5 office_pass_switch_main кнопка L2 → toggle light.smart_light_office_right ⚠️
1771466806839 Темно: вкл. подсветку лестницы illuminance below: 20light.light_stairs_left+_right turn_on
1771466955010 Светло: выкл. подсветку лестницы illuminance above: 60light.light_stairs_left+_right turn_off
1771683420621 Toggle Dimmer bed кнопка remote_button_short_presslight.toggle light.bed_dimmer
1771683677259 Dimmer bed cycle кнопка long_presslight.turn_on brightness 50 % на light.bed_dimmer
1771997851260 Вкл. ночной свет душевая присутствие + темно → light.dushevaia_night_light on
1771997918348 Выкл. ночной свет душевая нет присутствия / светло → off
1773451257968 Протечка котельная moistnotify.notify
88000000000008800000000011 Батарея: 12 шт. см. §6
heating_cable_ctl_0001 Греющий кабель: управление time_pattern /15 + numeric_state ZONT below 8 + отвал датчика → choose 5 веток на switch.heating_cable_plug. См. §7

🔄 Переименовано 2026-09-15: ссылки на light.night_light_shower_2light.dushevaia_night_light (id 1771997851260, 1771997918348). Удалены из карты: 1773451323415 «Датчик протечки котельная батарея» и 1773459663218 «Zigbee T sensor батарея» — заменены 12 единообразными (§6). Тела удалены, осиротевшие записи реестра вычищены.

⚠️ Два сценария подсветки лестницы (id 1771466806839 / 1771466955010) удалены как «призраки» и ПЕРЕСОЗДАНЫ с теми же id на sensor.light_sensor_stairs_illuminance. Пороги 20/60. 🗑 Удалённые «призраки», которых в файле НЕТ: automation.svetlo_vykl_osveshchenie_lestnitsy, automation.temno_vkl_podsvetku_lestnitsy, automation.datchik_osveshchennosti_lestnitsa_batareia, automation.light_switch_bed_batareia.

Образец правильной защиты от дребезга: «Темно/Светло подсветка лестницы» использует гистерезис (below: 20 / above: 60). Брать как эталон.

2.1. 📊 Разбор: почему ВЫКЛ сработал «рано» (2026-09-16, 07:50 местного)

Вопрос Alex: «Почему сценарий выключения подсветки лестницы уже сработал? Разве уже достаточно светло?»

Ответ: сработал ПРАВИЛЬНО. Причина — рассвет, не лампа.

UTC Местное (UTC+7) lx Событие
23:46 (15.09) 06:46 2 рассвет начался, датчик пошёл с нуля
00:49:37 07:49 46 последнее «тёмное» значение
00:50:21 07:50 54
00:50:33 07:50 59
00:50:45 07:50 63 порог 60 пересёкся → light.turn_off
00:50:46 07:50 light.light_stairs_left + _rightoff
00:54:23 07:54 103 текущее

Как отличить рассвет от лампы по истории: за 6 ч датчик прошёл 0 → 2 → 7 → 10 → 13 → … → 63 — ровный монотонный подъём. Лампа дала бы скачок в сотни люкс за секунды. Плюс last_triggered ВЫКЛ = 00:50:45.546, а лампа выключилась в 00:50:46 — совпадение до миллисекунд.

Диагностика (воспроизводимо): ~/tmp-t610/stairs_ctx.sh на t610 — состояние + история 6 ч + last_triggered обеих автоматизаций за один прогон.

⚠️ Остаточный риск (не залипание, а дребезг): окно гистерезиса 20–60 lx узкое по времени — здесь переход 59→63 занял 24 секунды. В пасмурную погоду или при медленном рассвете (после 21–22 сентября день укорачивается) освещённость будет колебаться вокруг порога, и подсветка начнёт хлопать: каждое падение <20 → вкл, каждый подъём >60 → выкл. Предложенный фикс (ждёт решения Alex — правка боевого конфига): расширить окно до below: 15 / above: 80. Тогда утренний переход — секунды, а не минуты, и дребезг исключён. 📌 На 2026-09-16 не залипло и не хлопало — фикс превентивный, не срочный.


3. 🔴 ДЕФЕКТ: свет кабинета мигает при перезагрузке HA

Симптом: при каждом рестарте HA свет в кабинете мигает.

Первопричина: триггеры office_pass_switch_table / office_pass_switch_main используют platform: state без to/from → срабатывают на любую смену состояния сущности кнопки.

При старте HA кнопки проходят unavailable → unknown → on — каждый переход дёргает light.toggle. Подтверждено историей: switch.office_table_light_switch_l1 сменил состояние 3 раза в течение 5 минут в момент рестартов HA (05:16:36 → unavailable, 05:16:40 → on, 05:18:39 → unavailable, 05:18:46 → unknown, 05:19:20 → on).

Текущий конфиг (сырой):

{
  "alias": "office_pass_switch_table",
  "mode": "restart",
  "trigger": [{"platform": "state", "entity_id": ["switch.office_table_light_switch_l1"]}],
  "action": [{"service": "light.toggle", "target": {"entity_id": "light.smart_light_office_left"}}]
}

Направление фикса: привязать триггер к конкретному состоянию (to:) ИЛИ заменить на триггер по событию нажатия. Требует определить фактическое поведение кнопки при физическом нажатии (сейчас кнопки в on — это состояние после старта, не нажатие).

Статус: ⚠️ НЕ ИСПРАВЛЕНО. Требует замера поведения кнопки при нажатии.


4. Ночной свет душевой 2

Сущности (device_id 4095e7c3b47b9dc9640cfb8c3aeff022 = радар, 4d6e55505ff7dbad13d2674cdcb18d5a = лампа):

  • sensor.shower_2_presence_sensor_illuminance — освещённость, lx
  • binary_sensor.shower_2_presence_sensor_presence — занятость
  • light.dushevaia_night_lightсамо реле (его дёргает автоматизация; бывш. light.night_light_shower_2)

📌 2026-09-15: хелпер switch.night_light_shower_2 (switch_as_x) снесён при переезде на ZHA. Осталась одна сущность — light.dushevaia_night_light. Автоматизации ссылаются именно на неё.

Автоматизации:

id Триггер Условие
ВКЛ 1771997851260 illuminance below: 6 + occupied below: 6 + is_occupied
ВЫКЛ 1771997918348 not_occupied + illuminance above: 6 ⚠️ conditions: [] — пусто

История правки (2026-09-15): порог 8 → 6. Порог 8 лежал ровно в центре дребезга датчика (7 ↔ 12), рабочий диапазон датчика всего 0…15 lx → ВКЛ/ВЫКЛ хлопали по кругу.

⚠️ Остаточный риск (принят Alex): при 2 lx (покой) ВЫКЛ ждёт above: 6 и может не наступить → свет залипнет. Гистерезис сознательно не сделан. Если залипнет — варианты: ① гистерезис вкл below 4 / выкл above 10; ② гасить по факту включения основной лампы (блокер: сущность основного освещения верхней душевой неизвестна); ③ калибровка illuminance_calibration радара.

Параметры радара: fading_time = 10 с, maximum_range = 2.85 м — проверять, покрывает ли зона всю душевую.


5. 🔋 Контроль батарей — 12 автоматизаций (2026-09-15)

Единый шаблон, id 88000000000008800000000011:

- id: '8800000000000'
  alias: 'Батарея: <место>'
  description: 'Контроль заряда Zigbee-датчика. Порог 20%, выдержка 2ч.'
  triggers:
  - trigger: numeric_state
    entity_id: sensor.<device>_battery
    below: 20
    for: '02:00:00'
    id: low
  - trigger: numeric_state
    entity_id: sensor.<device>_battery
    above: 20
    id: ok
  conditions: []
  actions:
  - choose:
    - conditions: [{condition: trigger, id: low}]
      sequence:
      - action: persistent_notification.create
        data:
          title: '🔋 Батарея разряжена'
          message: '<место> — заряд {{ states(''sensor.<device>_battery'') }}%. Замените батарейку.'
          notification_id: bat_<slug>
      - action: notify.mobile_app_sm_s931b
        data:
          title: '🔋 Батарея разряжена'
          message: '<место> — заряд {{ states(''sensor.<device>_battery'') }}%. Замените батарейку.'
    - conditions: [{condition: trigger, id: ok}]
      sequence:
      - action: persistent_notification.dismiss
        data: {notification_id: bat_<slug>}
  mode: single
id Место Сущность заряда
8800000000000 Туалет 1 этаж (тёплый пол) sensor.toilet_1_floor_temperature_battery
8800000000001 Лестница (освещённость) sensor.light_sensor_stairs_battery
8800000000002 Душевая (тёплый пол) sensor.dushevaia_floor_temperature_battery
8800000000003 Котельная (протечка) sensor.boiler_water_leak_battery
8800000000004 Гардеробная (температура) — датчик перенесён из Кабинета 2026-09-15 sensor.garderobnaia_temperature_battery
8800000000005 Гостиная (тёплый пол) sensor.living_room_floor_temperature_battery
8800000000006 Серая (тёплый пол) sensor.severnaia_floor_temperature_battery
8800000000007 Кабинет (тёплый пол) sensor.kabinet_floor_temperature_battery
8800000000008 Кухня (тёплый пол) sensor.kitchen_floor_temperature_battery
8800000000009 Ванная (тёплый пол) sensor.vannaia_floor_temperature_battery
8800000000010 Прихожая (тёплый пол) sensor.prikhozhaia_floor_temperature_battery
8800000000011 Спальня (кнопка) sensor.wireless_light_switch_bed_battery

5.1. 🔴 Питфоллы

  1. Выдержка for: '02:00:00' обязательна. Батарейные TS0201 под нагрузкой дают кратковременные просадки — без выдержки прилетают ложные алерты.
  2. Двойной канал. persistent_notification = видно в UI; notify.mobile_app_sm_s931b = push на телефон. Alex: «Видимо persistent с пушом на тел».
  3. Второй триггер ok (above: 20) гасит уведомление через notification_id — иначе алерт висит вечно. Для этого у persistent_notification.create обязателен notification_id: bat_<slug>.
  4. 🔴 Сирота-автоматизация: unavailable при чистом YAML. Тело удалено из automations.yaml, запись в реестре осталась. Удаление:
    # Если config/entity_registry/remove падает с id_reuse:
    #   "Identifier values have to increase"
    {"type":"config/entity_registry/update","entity_id": E, "disabled_by": "user"}  # сначала
    {"type":"config/entity_registry/remove","entity_id": E}                          # потом
    
    Реальный случай: после удаления тел 1773451323415 / 1773459663218 обе сущности остались unavailable.
  5. ⚠️ sensor.wireless_light_switch_bed_battery = unknown — кнопка спальни спит. Автоматизация создана, но триггера не будет, пока датчик не проснётся (лечится нажатием кнопки).
  6. ⚠️ Проверять имена сущностей после переименований: в ZHA домен бывает _batareia, но trigger: battery_level (не battery).
  7. 🔴 id_reuse: Identifier values have to increase при переименовании сущности (проверено 2026-09-15: 6 из 7 упало, хотя целевые entity_id свободны). Это внутренний счётчик реестра, не поломка. Обход — промежуточное имя:
    {"type":"config/entity_registry/update","entity_id": old, "new_entity_id": tmp}  # dom.tmp_xxx
    {"type":"config/entity_registry/update","entity_id": tmp, "new_entity_id": new}
    # при провале 2-го шага — откат: tmp → old
    
    Общий принцип: любая операция реестра, падающая с id_reuse, лечится промежуточным состоянием (для сирот-автоматизаций — disabled_by: user, см. питфолл 4).
  8. 🔴 HA перегенерирует entity_id автоматизаций по alias при reload. Проверять/искать по attributes.id, не по entity_id. Пример: automation.datchik_protechki_kotelnaia_batareiaautomation.batareia_kotelnaia_datchik_protechki.

Скрипты: ~/tmp-t610/add_battery_autos.py (генератор), ~/tmp-t610/rm_orphans.py, rm_orphan2.py, rm_orphan3.py, gard_fix.py (правка ссылок при переносе датчика), fix_aut_entid.py (правка entity_id автоматизации).

⚠️ ОТКРЫТО (на 2026-09-15): Alex сообщил, что HA ругается на «Office temperature sensor battery — нет уникального id, устройство недоступно». Проверено: призрака в бэкенде НЕТ (нет в /api/states, нет в реестре из 572 записей, MQTT-сущностей без unique_id — 0, config entries все loaded). Вероятный источник — UI-кэш или дашборд-карточка. Нужно уточнить у Alex экран (Настройки → Устройства → MQTT / дашборд / Developer Tools) и вычистить прицельно.

📄 Полный отчёт о работах: family/plans/t610-zigbee-ids-battery-freshsensors-modbus


6. Диагностика

# История значения (доказательство дребезга)
T=$(date -u -v-3H '+%Y-%m-%dT%H:%M:%S')
curl -s -H @/tmp/h1 "$B/api/history/period/$T+00:00?filter_entity_id=<entity>&minimal_response&no_attributes" \
  | jq -r '.[0][] | "\(.last_changed) -> \(.state)"'

# Состояния сущностей устройства (через шаблон)
curl -s -X POST -H @/tmp/h1 -H "Content-Type: application/json" \
  -d '{"template":"{% set dev = device_id(\"sensor.x\") %}{% for e in device_entities(dev) %}{{ e }} = {{ states(e) }}\n{% endfor %}"}' \
  "$B/api/template"

Артефакты: ~/tmp-t610/fix_shower_light_threshold.sh (образец правки порога), ~/tmp-t610/ha_ws.py (WS-реестры). Бэкап: ~/tmp-t610/automations/automations.yaml.bak-20260915-105455.


7. Греющий кабель: управление (2026-09-16)

Автоматизация: automation.greiushchii_kabel_upravlenie · id heating_cable_ctl_0001 · mode: single · одна на все случаи (0 helper'ов).

Управляет: switch.heating_cable_plug (NEO NAS-WR01B, a4c138eb6fbe9d19, котельная) — розетка греющего кабеля ввода воды. Кабель саморегулирующийся.

Порог 8 °C — по расчёту промерзания бетонной подушки 30 см (Новосибирск, суглинок, труба на 4 м). Выдержка 12 ч = тепловая инерция бетона. Прогноз привлекается потому, что ZONT — одна точка и может отвалиться.

Ветки логики

Ветка choose Условие Действие
1 eff ≤ 20 switch.turn_on + persistent + push (без выдержки)
2 not zont_ok switch.turn_on + push (fail-safe)
3 eff ≤ 8 + розетка off 12 ч switch.turn_on + persistent + push
4 eff ≥ 3 + розетка on switch.turn_off + persistent
5 розетка on + power < 5 W 10 мин push «нет потребления»

Триггеры: time_pattern /15 + numeric_state ZONT below 8 + state ZONT → unknown/unavailable 30 мин. где eff = ZONT-улица, если датчик жив, иначе прогноз-мин на 24 ч.

Конструкция — прогноз как ДЕЙСТВИЕ, не как шаблон:

actions:
  - action: weather.get_forecasts          # сервис во МНОЖЕСТВЕННОМ числе
    target: {entity_id: weather.forecast_laki_dom}
    data: {type: hourly}
    response_variable: wx
  - variables:                              # ПОСЛЕ вызова — forecast уже есть
      zont_ok: "{{ states('sensor.h2000_pro_temperatura_ulitsa') not in ['unknown','unavailable','none',''] }}"
      fc_min: "{{ wx['weather.forecast_laki_dom']['forecast'][:24] | map(attribute='temperature') | min | float(-100) }}"
      eff: "{{ zont if zont_ok else fc_min }}"
  - choose: [...]                           # 5 веток

🔴 Питфоллы этой автоматизации (все три поймал проверкой, без неё ушли бы в прод):

  1. weather.get_forecasts — ТОЛЬКО как action: + response_variable. В Jinja-шаблоне → 'weather' is undefined; атрибута forecast у сущности нет. Имя — множественное число; weather.get_forecast (ед. ч.) не существует.
  2. variables: вычисляются ДО действий. fc_min из response_variable можно объявить только внутри actions:, после вызова сервиса.
  3. Fail-safe нельзя строить на min([99, fc_min])min([99, 11.1]) = 11.1, условие не сработает никогда. Нужен отдельный флаг zont_ok по строковому состоянию.

⚠️ Выдержка 12 ч реализована через for: '12:00:00' на switch.heating_cable_plug (состояние), НЕ на шаблон — for: нельзя вешать на вычисляемое значение. ⚠️ WS render_template возвращает null даже для {{ 2 + 2 }} — шаблоны проверять только через REST POST /api/template. ⚠️ automation.trigger через WS не обновляет last_triggered — прогон подтверждать иначе (значения в variables, лог HA).

📄 План (выполнен): family/plans/t610-heating-cable-automation.md 🧰 Инструменты: ~/tmp-t610/ha_ws.py forecast <entity> <daily|hourly>, verify_cable.py, verify_calc.sh, rest_tpl.sh.

⚠️ Не проверено физически

Розетка ни разу не включалась — off, 0 W / 0 kWh, voltage 223 V. Это норма для выключенной розетки, не поломка. Что кабель реально греет — не подтверждено: нужен ручной прогон switch.heating_cable_plug на 10 мин и проверка sensor.heating_cable_plug_power > 0.


Связанные