# ТЗ: Дашборд доступности устройств в Home Assistant ## Цель Дашборд, показывающий **uptime устройств** в процентах за три периода: день, неделю, месяц. С сортировкой по убыванию доступности. ## Метрика **Доступность** = время в статусе «доступен» / общее время периода × 100% - «Доступен» — любой статус, кроме `unavailable` и `unknown`. **Важно:** статус `off` (выключенное устройство) считается доступным — выключено ≠ недоступно - «Недоступен» — статус `unavailable` или `unknown` - Результат: процент (0%–100%), один знак после точки ## Периоды По умолчанию: **7 дней**. В дашборде — переключатель: 24ч / 7д / 30д. | Период | Обновление | Описание | |--------|------------|----------| | 24ч | каждые 5 мин | Оперативный мониторинг | | 7д | каждые 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_` с атрибутами ### 2. Структура sensor-ов Каждый отслеживаемый device → один sensor с атрибутами: ```yaml 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_token` → `ws://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/?filter_entity_id=&minimal_response&no_attributes` - Возвращает массив пар `(state, last_changed)` — достаточно для расчёта времени - AppDaemon подключён к HA через WebSocket — использует `self.get_entity_history()` или REST API через `self.hass` ### Расчёт (алгоритм) ```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_` (один sensor, период в атрибуте) - Атрибуты: `entity_id`, `period`, `availability_pct`, `area`, `down_count`, `max_downtime_minutes`, `sparkline`, `trend`, `last_downtime`, `color`, `last_updated` - Группировка по комнатам: `sensor.avail_area_` - Прогресс расчёта: `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:** ```yaml hello_world: module: hello class: HelloWorld availability: module: availability class: Availability ha_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-дашборд - Расширить на другие домены (после обкатки)