# ТЗ: Дашборд доступности устройств в Home Assistant ## Цель Дашборд, показывающий **uptime устройств** в процентах за три периода: день, неделю, месяц. С сортировкой по убыванию доступности. ## Метрика **Доступность** = время в статусе «доступен» / общее время периода × 100% - «Доступен» — любой статус, кроме `unavailable` и `unknown` - «Недоступен» — статус `unavailable` или `unknown` - Результат: процент (0%–100%), один знак после точки ## Периоды | Период | Формула | Обновление | |--------|---------|------------| | День | за последние 24 часа | каждые 5 мин | | Неделя | за последние 7 дней | каждые 30 мин | | Месяц | за последние 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. Вычисление доступности — через长期的 sensor Для каждого отслеживаемого устройства создать **template sensor** (или один Python-скрипт через REST), который: 1. Запрашивает историю из `/api/history/period/?filter_entity_id=&minimal_response&no_attributes` 2. Считает суммарное время в `unavailable`/`unknown` 3. Вычисляет процент доступности 4. Записывает результат как sensor state ### 2. Варианты реализации #### Вариант A: Utility Meter + Template Sensor (нативный HA) - На каждое устройство — `binary_sensor` доступности (доступен/нет) - `utility_meter` считает время в каждом статусе - Template sensor вычисляет процент - ❌ **Минус:** нужен `custom:utility_meter` или `history_stats` — много entity на каждое устройство, сложная конфигурация при 180+ устройствах #### Вариант B: Python-скрипт + AppDaemon (рекомендуемый) - Один Python-скрипт, работающий по расписанию - Читает историю через HA API для всех устройств сразу - Вычисляет доступность за все 3 периода - Создаёт/обновляет `sensor.availability__` через `states` API - ✅ **Плюсы:** один скрипт, легко масштабировать, минимальная нагрузка на HA - ⚠️ **Требует:** AppDaemon или HA Python script / custom component #### Вариант C: Custom HA Integration (HACS) - Кастомная интеграция, регистрирует sensor-ы через `async_setup_platform` - Берёт данные из recorder напрямую (SQL) - ✅ **Плюсы:** максимально нативно, работает через recorder без API - ⚠️ **Минусы:** сложнее разработка, нужен доступ к БД recorder #### Вариант D: SQL Sensor + Jinja (самый простой) - `sql` integration — прямые запросы к MariaDB/SQLite recorder - Один sensor на устройство×период с SQL-запросом - ✅ **Плюсы:** без внешних зависимостей, работает из коробки - ❌ **Минусы:** 180 устройств × 3 периода = 540 SQL-запросов по расписанию — нагрузка на БД ### Рекомендация: Вариант B (AppDaemon / Python-скрипт) Оптимальный баланс сложности и производительности. Один скрипт, batch-обработка, минимальная нагрузка. ## Дашборд ### Карточка: таблица доступности ``` ┌──────────────────────────┬────────┬────────┬────────┐ │ Устройство │ День │ Неделя │ Месяц │ ├──────────────────────────┼────────┼────────┼────────┤ │ 💡 Бра в спальне │ 99.8% │ 98.5% │ 95.2% │ │ 🔌 Лесной колодец насос │ 100.0% │ 99.1% │ 97.8% │ │ 🌡️ Улица температура │ 85.3% │ 92.1% │ 88.7% │ │ ... │ ... │ ... │ ... │ └──────────────────────────┴────────┴────────┴────────┘ ``` **Функциональность:** - Сортировка по любому столбцу (по умолчанию — по месячной доступности, убывание) - Цветовая индикация: - 🟢 99-100% — зелёный - 🟡 95-99% — жёлтый - 🟠 90-95% — оранжевый - 🔴 <90% — красный - Фильтр по домену (switch, sensor, light...) - Обновление: при загрузке дашборда + по расписанию ### Карточка: сводка ``` ┌──────────────────────────────────┐ │ 📊 Доступность устройств │ │ │ │ Средняя за день: 97.3% │ │ Средняя за неделю: 96.1% │ │ Средняя за месяц: 94.8% │ │ │ │ Устройств <95% (месяц): 5 │ │ Устройств <90% (месяц): 2 │ └──────────────────────────────────┘ ``` ## Технические детали ### Источник данных - HA History API: `/api/history/period/?filter_entity_id=&minimal_response&no_attributes` - Возвращает массив пар `(state, last_changed)` — достаточно для расчёта времени ### Расчёт (алгоритм) ```python 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__` - Атрибуты: `entity_id`, `period`, `availability_%`, `last_updated` - Группировка: `group.availability_day`, `group.availability_week`, `group.availability_month` ### Нагрузка - ~180 устройств - History API batch-запрос (можно передать несколько entity_id через запятую) - За день: ~180 × 3 = 540 точек данных (при batch — 3 API-вызова) - За неделю/месяц: данные уже рассчитаны, обновляются реже ## Ограничения - `purge_keep_days` в recorder — по умолчанию 10 дней. Для месячной статистики нужно **увеличить до 35 дней** - Если устройство добавлено недавно — показывать доступность с момента добавления (не с начала периода) - Если устройство удалено — перестать показывать на дашборде ## Что нужно от Славы 1. Подтвердить вариант реализации (рекомендую B — AppDaemon/Python) 2. Увеличить `purge_keep_days` до 35 (иначе не будет данных за месяц) 3. Установить AppDaemon (если выбран вариант B) — или подтвердить другой вариант 4. Подтвердить список исключений (какие entity не отслеживать)