Files
wiki/tasks/ha-availability-dashboard/TZ.md
2026-04-15 14:50:01 +03:00

19 KiB
Raw Blame History

ТЗ: Дашборд доступности устройств в Home Assistant

Цель

Дашборд, показывающий uptime устройств в процентах за три периода: день, неделю, месяц. С сортировкой по убыванию доступности.

Метрика

Доступность = время в статусе «доступен» / общее время периода × 100%

  • «Доступен» — любой статус, кроме unavailable и unknown. Важно: статус off (выключенное устройство) считается доступным — выключено ≠ недоступно
  • «Недоступен» — статус unavailable или unknown
  • Результат: процент (0%100%), один знак после точки

Периоды

По умолчанию: 7 дней. В дашборде — переключатель: 24ч / 7д / 30д.

Период Обновление Описание
24ч каждые 5 мин Оперативный мониторинг
каждые 15 мин Основной режим по умолчанию
30д каждые 2 часа Долгосрочная статистика

Переключение периода пересчитывает все данные на лету из истории. Храним кэш для всех трёх периодов, чтобы переключение было мгновенным.

Какие устройства отслеживать

Все entity из доменов физических устройств:

  • binary_sensor, sensor, switch, light
  • climate, cover, lock, media_player
  • device_tracker, vacuum, fan, humidifier
  • water_heater, siren, button

Исключения (не показывать на дашборде):

  • Вспомогательные entity Zigbee2MQTT (sensor.*_update_state, update.*, button.zigbee2mqtt_*)
  • Entity с суффиксами _battery_low, _battery, _linkquality, _update, _identify
  • Виртуальные/template сенсоры (без device_id)
  • Автоматически создаваемые Select/Number для Zigbee-устройств (настройки radar_sensitivity и т.п.)

Архитектура решения

1. Вычисление доступности

Один Python-скрипт (AppDaemon или HA Python custom component):

  1. Периодически (по расписанию для каждого периода) запрашивает историю из HA API
  2. Batch-запрос: несколько entity_id через запятую (filter_entity_id=id1,id2,id3)
  3. Считает для каждого устройства:
    • Процент доступности за выбранный период
    • Частоту падений (количество переходов в unavailable/unknown)
    • Максимальный непрерывный даунтайм
    • Sparkline данные (7 точек — по одной за день)
    • Сравнение с предыдущим периодом (тренд)
  4. Записывает результаты как sensor-ы: sensor.avail_<sanitized_id> с атрибутами

2. Структура sensor-ов

Каждый отслеживаемый device → один sensor с атрибутами:

sensor.avail_light_bra_v_spalne:
  state: 97.8  # текущий % доступности
  attributes:
    entity_id: light.bra_v_spalne
    friendly_name: "Бра в спальне"
    domain: light
    period: 7d  # выбранный период
    availability_pct: 97.8
    down_count: 5
    max_downtime_minutes: 102
    sparkline: [100, 100, 98, 95, 99, 100, 97.8]  # 7 точек
    trend: "down"  # down/up/stable vs прошлая неделя
    last_downtime: "2026-04-14T15:32:00+03:00"
    color: "green"  # green/yellow/orange/red

3. Варианты реализации

Вариант A: AppDaemon (рекомендуемый)

  • Отдельный Python-скрипт в AppDaemon
  • Работает по расписанию, batch-обработка
  • Легко читать/обновлять, не зависит от HA Core
  • Плюсы: изоляция, легко отлаживать, минимум нагрузки на HA
  • ⚠️ Требует: установка AppDaemon как HA addon

Вариант B: HA Custom Component

  • Кастомная интеграция, регистрирует dynamic sensor-ы
  • Работает как часть HA, доступ к recorder напрямую
  • Плюсы: нативно, без доп. зависимостей
  • ⚠️ Минусы: сложнее разработка, перезагрузка при изменениях

Вариант C: HA Python Scripts + REST Command

  • Python-скрипт через python_script integration
  • Запускается по automation, сохраняет результаты через REST
  • Плюсы: без AppDaemon
  • Минусы: python_script ограничен в импортах, сложнее batch

Рекомендация: Вариант A (AppDaemon) ВЫБРАН

Оптимальный баланс. Изолированный Python-скрипт, batch-обработка, легко масштабировать.

Установлено: AppDaemon 4.5.13, HA 2026.4.2, Python 3.12.13 Путь конфига: /addon_configs/a0d7b954_appdaemon/appdaemon.yaml Путь приложений: /addon_configs/a0d7b954_appdaemon/apps/ Путь apps.yaml: /addon_configs/a0d7b954_appdaemon/apps/apps.yaml Slug аддона: a0d7b954_appdaemon Подключение к HA:

  • REST API: автоматическое через SUPERVISOR_TOKEN (http://supervisor/core/api)
  • WebSocket (для registry): HA Long-Lived Access Token через apps.yaml аргумент ha_tokenws://homeassistant:8123/api/websocket
  • ⚠️ SUPERVISOR_TOKEN не подходит для прямого WebSocket-подключения к HA — только для REST через supervisor proxy

Первая фаза: только light + switch

Начинаем с минимального набора — только домены light и switch. После обкатки расширяем на остальные.

Устройств после фильтрации: ~32 (2 light + 30 switch)

Дополнительные исключения для switch (настройки реле — не основные устройства):

  • *_delayed_power_on_state
  • *_detach_relay_mode
  • *_network_indicator
  • *_turbo_mode
  • *_do_not_disturb
  • switch.zigbee2mqtt_bridge_permit_join

После этих исключений — ~18 основных устройств.

Cold-start (первичная загрузка и перезапуск)

При запуске AppDaemon (или после перезапуска HA) — полный пересчёт текущего активного периода для всех устройств. Не ждать следующего расписания — данные должны быть актуальны сразу.

Rate-limit при batch-запросах: 180 устройств × 30 дней истории — ощутимая нагрузка на HA API. Разбивать на батчи по ~20 entity_id с паузой 1 сек между запросами. Показывать прогресс: sensor.avail_calc_progress с состоянием "47/180" или "idle".

Дашборд

Группировка по комнатам/зонам

Устройства группируются по HA areas (комнатам). Если у устройства нет area — группа «Без комнаты».

  • Каждая группа — сворачиваемая секция с заголовком: 🛏️ Спальня (12 устройств, 97.2%)
  • В заголовке: иконка комнаты, средняя доступность по группе, количество устройств
  • По умолчанию: проблемные комнаты (средний % < 95%) раскрыты, остальные свёрнуты
  • Сортировка комнат: сначала проблемные, потом по средней доступности (по убыванию)

Мобильная адаптация

На экранах < 768px (мобильный вид):

  • Убрать sparkline — не помещается
  • Компактная строка: иконка + friendly name + цветной процент (крупный)
  • Прогресс-бар — тонкий, одна линия (не блок)
  • Сводка падений — скрыта, раскрывается по тапу (expandable)
  • Группы комнат — свёрнуты по умолчанию, тап раскрывает список

Десктопный вид — как описано ниже (полный).

Карточка: список устройств с визуалом (десктоп)

Прогресс-бар + сводка для каждого устройства:

💡 Бра в спальне          ████████████████████░  97.8%
                           📉 100 100 98 95 99 100 98
                           5 падений, макс. даунтайм 1ч 42мин

Строка устройства:

  • Friendly name + иконка домена (💡, 🔌, 🌡️ и т.д.)
  • Прогресс-бар — заполненность = доступность (%). Цвет по диапазону:
    • 🟢 ≥99% — зелёный
    • 🟡 95-99% — жёлтый
    • 🟠 90-95% — оранжевый
    • 🔴 <90% — красный
  • Процент — справа от прогресс-бара, крупный шрифт
  • Sparkline — мини-график из 7 точек под прогресс-баром, тренд стрелкой (📈/📉/➡️)
  • Сводка падений — одна строка: N падений, макс. даунтайм Xч Yмин
  • Время последнего падения — если <24ч: "X минут/часов назад"

Переключатель периода — вверху дашборда: [24ч] [7д] [30д]. По умолчанию 7д.

Карточка: сводка

┌─────────────────────────────────────────┐
│ 📊 Доступность за 7 дней                │
│                                         │
│ ██████████████████████░  Средняя: 96.4% │
│                                         │
│ 🔴 Проблемных (<95%): 4                │
│    💡 Бра в спальне — 87.2%            │
│    🔌 Б.колодец насос — 91.3%          │
│    🌡️ Ванна температура — 82.7%        │
│    🔒 Замок входной — 94.1%            │
│                                         │
│ 📉 Хуже чем прошлую неделю: 3           │
│ 📈 Лучше чем прошлую неделю: 8         │
└─────────────────────────────────────────┘

Функциональность:

  • Проблемные (<95%) — отдельная сворачиваемая секция
  • Тренд: сравнение с предыдущим периодом (предыдущие 7 дней)
  • Клик на проблемное устройство — скролл к нему в таблице
  • Цветовая индикация средней доступности (прогресс-бар в сводке)
  • Кнопка "Обновить" — принудительный пересчёт

Технические детали

Путь приложения AppDaemon

/addon_configs/a0d7b954_appdaemon/apps/
├── apps.yaml              # регистрация приложений
├── availability.py        # основной модуль расчёта доступности
└── availability_utils.py  # вспомогательные функции (фильтрация, форматирование)

Источник данных

  • HA History API: /api/history/period/<start>?filter_entity_id=<ids>&minimal_response&no_attributes
  • Возвращает массив пар (state, last_changed) — достаточно для расчёта времени
  • AppDaemon подключён к HA через WebSocket — использует self.get_entity_history() или REST API через self.hass

Расчёт (алгоритм)

def calc_availability(history_entries, period_start, period_end):
    unavailable_seconds = 0
    for entry in history_entries:
        state = entry['state']
        changed = parse_datetime(entry['last_changed'])
        
        if state in ('unavailable', 'unknown'):
            # Сколько времени устройство было в этом статусе
            # до следующего изменения или до period_end
            next_change = get_next_change(entry) or period_end
            unavailable_seconds += (next_change - changed).total_seconds()
    
    total_seconds = (period_end - period_start).total_seconds()
    availability = (1 - unavailable_seconds / total_seconds) * 100
    return round(availability, 1)

Хранение результатов

  • Каждый результат — sensor.avail_<sanitized_id> (один sensor, период в атрибуте)
  • Атрибуты: entity_id, period, availability_pct, area, down_count, max_downtime_minutes, sparkline, trend, last_downtime, color, last_updated
  • Группировка по комнатам: sensor.avail_area_<sanitized_name>
  • Прогресс расчёта: sensor.avail_calc_progress ("47/180" или "idle")

Дашборд: имеющиеся кастомные карточки

Установлены через HACS:

  • mini-graph-card — для sparkline
  • auto-entities — автоподхват устройств
  • card-mod — кастомный CSS (мобильная адаптация)
  • stack-in-card — группировка карточек

Нужно доустановить:

  • button-card — для строк устройств с прогресс-баром
  • custom:hui-element — для input_select на дашборде (или использовать стандартный entities card)

AppDaemon: реализация

Модули:

  • availability.py — класс Availability(hass.Hass), основная логика
  • availability_utils.py — чистые функции (фильтрация, расчёт, форматирование)

Ключевые решения при реализации:

  • self.sleep() в AppDaemon 4.x — coroutine, нельзя вызывать синхронно → заменён на time.sleep() (блокирует только worker thread)
  • log_level: info в apps.yaml вызывает ValueError: Unknown level → убрать, AppDaemon использует INFO по умолчанию
  • HA Registry API (area/entity/device) недоступен через REST — только WebSocket (config/area_registry/list и т.д.)
  • SUPERVISOR_TOKEN работает для REST через supervisor proxy, но не для прямого WS к HA
  • Entity ID не может содержать кириллицу → sanitize_area_name() с транслитерацией (а→a, б→b, ...)

apps.yaml:

hello_world:
  module: hello
  class: HelloWorld

availability:
  module: availability
  class: Availability
  ha_token: <Long-Lived Access Token>  # для WebSocket registry

⚠️ Секреты: ha_token хранится в apps.yaml на HA. Не дублировать в других файлах.

Нагрузка

  • ~180 устройств
  • History API batch-запрос (можно передать несколько entity_id через запятую)
  • За день: ~180 × 3 = 540 точек данных (при batch — 3 API-вызова)
  • За неделю/месяц: данные уже рассчитаны, обновляются реже

Ограничения

  • purge_keep_days в recorder — по умолчанию 10 дней. Для месячной статистики нужно увеличить до 35 дней
  • Если устройство добавлено недавно — показывать доступность с момента добавления (не с начала периода)
  • Если устройство удалено — перестать показывать на дашборде

Что нужно от Славы

  1. Подтвердить вариант реализации AppDaemon (Вариант A)
  2. Увеличить purge_keep_days до 35 (иначе не будет данных за месяц)
  3. Установить AppDaemon Установлен (4.5.13)
  4. Подтвердить список исключений Согласовано (см. выше)
  5. Доустановить button-card через HACS
  6. Создать input_select.avail_period Создан (опции: 24h, 7d, 30d, по умолчанию 7d)
  7. Назначить areas устройствам в HA (Settings → Areas) — многие показывают «Без комнаты»
  8. Построить Lovelace-дашборд (после установки button-card)

Статус деплоя

Готово

  • AppDaemon 4.5.13 установлен и работает
  • availability.py + availability_utils.py задеплоены
  • 21 device sensor создан (sensor.avail_*)
  • 2 area sensor создан (sensor.avail_area_*)
  • input_select.avail_period создан
  • WebSocket registry работает (9 areas, 729 entity mappings)
  • Расчёт за все 3 периода (24h/7d/30d)

TODO

  • Назначить areas устройствам в HA
  • Увеличить purge_keep_days до 35
  • Установить button-card через HACS
  • Построить Lovelace-дашборд
  • Расширить на другие домены (после обкатки)