Files
wiki/tasks/ha-availability-dashboard/dashboard/availability.yaml
2026-04-15 15:20:01 +03:00

112 lines
5.2 KiB
YAML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
title: Доступность устройств
views:
- title: Доступность устройств
path: availability
cards:
# ═══ Переключатель периода ═══
- type: entities
entities:
- entity: input_select.avail_period
name: Период
style:
card:
padding: 8px 16px
# ═══ Кнопка обновить + статус ═══
- type: entities
entities:
- entity: sensor.avail_calc_progress
name: Статус
- entity: input_select.avail_period
name: Период (для обновления)
# ═══ Сводная карточка ═══
- type: custom:button-card
entity: sensor.avail_calc_progress
show_name: false
show_state: false
show_icon: false
custom_fields:
card: |
[[[
const devs = Object.entries(hass.states)
.filter(([k]) => k.startsWith('sensor.avail_') && !k.startsWith('sensor.avail_area_') && !k.startsWith('sensor.avail_calc_'))
.map(([k,v]) => ({ id: k, pct: parseFloat(v.state), attrs: v.attributes }));
if (!devs.length) return '<div style="padding:16px;color:var(--secondary-text-color);">Нет данных</div>';
const avg = (devs.reduce((s,d) => s + d.pct, 0) / devs.length).toFixed(1);
const problems = devs.filter(d => d.pct < 95).sort((a,b) => a.pct - b.pct);
const worse = devs.filter(d => d.attrs.trend === 'down').length;
const better = devs.filter(d => d.attrs.trend === 'up').length;
const avgClr = avg < 90 ? 'var(--error-color)' : avg < 95 ? 'var(--warning-color)' : avg < 99 ? '#f0c040' : 'var(--success-color)';
let h = `<div style="padding:16px;">`;
h += `<div style="font-size:13px;color:var(--secondary-text-color);margin-bottom:6px;">Средняя доступность</div>`;
h += `<div style="display:flex;align-items:center;gap:12px;margin-bottom:14px;">`;
h += `<div style="flex:1;position:relative;height:20px;background:var(--divider-color);border-radius:10px;overflow:hidden;">`;
h += `<div style="width:${avg}%;height:100%;background:${avgClr};border-radius:10px;transition:width .5s;"></div>`;
h += `</div>`;
h += `<div style="font-size:26px;font-weight:700;color:${avgClr};min-width:70px;text-align:right;">${avg}%</div>`;
h += `</div>`;
if (problems.length) {
h += `<div style="font-size:13px;font-weight:600;color:var(--error-color);margin-bottom:6px;">🔴 Проблемных (<95%): ${problems.length}</div>`;
problems.slice(0, 5).forEach(p => {
const ic = {switch:'🔌',light:'💡',binary_sensor:'',sensor:'',climate:'',cover:'',lock:'🔒',media_player:'🔊',device_tracker:'📍',vacuum:'🤖',fan:'🌀',humidifier:'💨',water_heater:'🚿',siren:'🚨',button:'🔘'}[p.attrs.entity_id?.split('.')[0]] || '📡';
const pc = p.pct < 90 ? 'var(--error-color)' : 'var(--warning-color)';
const ti = p.attrs.trend === 'down' ? '📉' : p.attrs.trend === 'up' ? '📈' : '➡️';
h += `<div style="display:flex;align-items:center;gap:6px;padding:2px 0;font-size:13px;">`;
h += `<span>${ic}</span><span style="flex:1;">${p.attrs.friendly_name || p.id.split('.')[1].replace(/_/g,' ')}</span>`;
h += `<span style="color:${pc};font-weight:600;">${p.pct}%</span><span>${ti}</span>`;
h += `</div>`;
});
}
h += `<div style="display:flex;gap:16px;margin-top:10px;font-size:12px;color:var(--secondary-text-color);">`;
h += `<span>📉 Хуже: ${worse}</span><span>📈 Лучше: ${better}</span>`;
h += `</div>`;
h += `</div>`;
return h;
]]]
styles:
card:
- padding: 0
- background: var(--card-background-color)
# ═══ Список всех устройств (автоматический) ═══
- type: custom:auto-entities
card:
type: entities
title: Устройства
filter:
include:
- entity_id: "sensor.avail_*"
exclude:
- entity_id: "sensor.avail_area_*"
- entity_id: "sensor.avail_calc_*"
sort:
method: state
numeric: true
reverse: true
card_mod:
style: |
ha-card {
margin-top: 8px;
}
ha-card .entity-row {
display: grid;
grid-template-columns: 40px 1fr 80px 40px;
align-items: center;
gap: 8px;
}
@media (max-width: 600px) {
ha-card .entity-row {
grid-template-columns: 30px 1fr 60px;
}
ha-card .entity-row .state {
display: none;
}
}