14 KiB
ТЗ: Дашборд доступности устройств в 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,lightclimate,cover,lock,media_playerdevice_tracker,vacuum,fan,humidifierwater_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):
- Периодически (по расписанию для каждого периода) запрашивает историю из HA API
- Batch-запрос: несколько entity_id через запятую (
filter_entity_id=id1,id2,id3) - Считает для каждого устройства:
- Процент доступности за выбранный период
- Частоту падений (количество переходов в unavailable/unknown)
- Максимальный непрерывный даунтайм
- Sparkline данные (7 точек — по одной за день)
- Сравнение с предыдущим периодом (тренд)
- Записывает результаты как 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_scriptintegration - Запускается по automation, сохраняет результаты через REST
- ✅ Плюсы: без AppDaemon
- ❌ Минусы:
python_scriptограничен в импортах, сложнее batch
Рекомендация: Вариант A (AppDaemon)
Оптимальный баланс. Изолированный Python-скрипт, batch-обработка, легко масштабировать.
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 дней)
- Клик на проблемное устройство — скролл к нему в таблице
- Цветовая индикация средней доступности (прогресс-бар в сводке)
- Кнопка "Обновить" — принудительный пересчёт
Технические детали
Источник данных
- HA History API:
/api/history/period/<start>?filter_entity_id=<ids>&minimal_response&no_attributes - Возвращает массив пар
(state, last_changed)— достаточно для расчёта времени
Расчёт (алгоритм)
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>_<period> - Атрибуты:
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 дней- Если устройство добавлено недавно — показывать доступность с момента добавления (не с начала периода)
- Если устройство удалено — перестать показывать на дашборде
Что нужно от Славы
- Подтвердить вариант реализации (рекомендую A — AppDaemon/Python)
- Увеличить
purge_keep_daysдо 35 (иначе не будет данных за месяц) - Установить AppDaemon (если выбран вариант A) — или подтвердить другой вариант
- Подтвердить список исключений (какие entity не отслеживать)