auto-sync: 2026-04-15 00:40:01
This commit is contained in:
156
tasks/ha-availability-dashboard/TZ.md
Normal file
156
tasks/ha-availability-dashboard/TZ.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# ТЗ: Дашборд доступности устройств в 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/<start>?filter_entity_id=<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_<entity_id>_<period>` через `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/<start>?filter_entity_id=<ids>&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_<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 дней**
|
||||
- Если устройство добавлено недавно — показывать доступность с момента добавления (не с начала периода)
|
||||
- Если устройство удалено — перестать показывать на дашборде
|
||||
|
||||
## Что нужно от Славы
|
||||
1. Подтвердить вариант реализации (рекомендую B — AppDaemon/Python)
|
||||
2. Увеличить `purge_keep_days` до 35 (иначе не будет данных за месяц)
|
||||
3. Установить AppDaemon (если выбран вариант B) — или подтвердить другой вариант
|
||||
4. Подтвердить список исключений (какие entity не отслеживать)
|
||||
Reference in New Issue
Block a user