14 KiB
aliases, created, namespace, related, tags, title, type, updated
| aliases | created | namespace | related | tags | title | type | updated | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
2026-09-17 | family |
|
|
📊 t610 — метрики хоста (RAM, температура) + лог, переживающий зависание | tech | 2026-09-17 |
📊 t610 — метрики хоста (RAM, температура) + лог, переживающий зависание
Статус: ✅ ВНЕДРЕНО И РАБОТАЕТ (2026-09-17). Локальный аддон
local_hw_metrics, интервал 60 с. Ждёт живой проверки Alex'ом (коммит ПОСЛЕ — после неё). Родительские доки: family/how-to/home-automation · расследование зависаний — family/tech/t610-hang-investigation
1. Зачем
Две задачи, поставленные Alex 2026-09-17:
- Метрики хоста в HA как датчики — сколько RAM занято/свободно, температура CPU.
- Лог, который переживает зависание. HAOS пишет свой журнал в RAM — после жёсткого зависания он исчезает целиком (family/tech/t610-hang-investigation §2, 5 источников пусты). Каждый инцидент стирал свои доказательства. Нужно было, чтобы следующий случай оставил следы.
2. Архитектура
аддон local_hw_metrics (alpine, bash, loop)
│ каждые 60 с
├─ читает ХОСТОВЫЕ /proc/meminfo, /proc/loadavg, /proc/pressure/*,
│ /sys/class/hwmon/hwmon0/temp1_input, /proc/uptime,
│ размер /config/home-assistant_v2.db + -wal
├─ публикует 11 сенсоров → POST /api/states/<entity> (HA REST API)
└─ дописывает строку в /share/ha-metrics/hw-YYYY-MM.log + sync
▲
└─ переживает жёсткое зависание
Почему аддон, а не cron в core_ssh: аддон core_ssh собран на s6 (/etc/services.d/ — только sshd, ttyd), а crond (BusyBox) в нём не запущен. /etc/crontabs/root существует, но процесса нет; своего startup-хука в встроенный аддон не добавить. Любая задача «внутри core_ssh» умрёт при рестарте аддона. Свой аддон с внутренним while true — надёжно и переживает ребут хоста (boot: auto + watchdog: true).
Почему REST, а не MQTT: из контейнера mosquitto_pub падает с Bad file descriptor (питфолл 7 родительской доки, подтверждён ещё раз). Единственный рабочий путь к HA из контейнера — внешний https://mallexxx.duckdns.org (проверено: 401 без токена, 200 с токеном). Значит POST /api/states/<entity> — и правка configuration.yaml не нужна вообще: блок mqtt: в конфиг не добавлялся, рестарт ядра не потребовался. Это лучше первоначального плана (было: MQTT → 6 сенсоров через mqtt:).
3. Файлы
| Что | Где |
|---|---|
| Аддон | /addons/hw_metrics/ на t610 — config.yaml, Dockerfile, metrics.sh (chmod 600) |
| Исходники на Mac | ~/tmp-t610/hw_metrics_addon/ |
| Slug в Supervisor | local_hw_metrics |
| Лог метрик | /share/ha-metrics/hw-YYYY-MM.log (хостовый, sda8) |
| Env-файл (легаси) | /etc/ha_hw_metrics.env — MQ_USER/MQ_PASS/TOK, chmod 600. Оставлен от первой (MQTT) попытки, не используется |
| Скрипт на хосте (легаси) | /usr/local/bin/ha_hw_metrics.sh — v2 (REST). Рабочий, но аддон его заменяет |
Версия токена: ha_token лежит в опциях аддона (/data/options.json внутри контейнера), tokenlen=183. Env-файл /etc/ha_hw_metrics.env — не нужен, оставлен как след первой попытки.
4. Датчики (11)
| Entity | Единица | Источник |
|---|---|---|
sensor.t610_ram_total |
MB | MemTotal |
sensor.t610_ram_available |
MB | MemAvailable |
sensor.t610_ram_free |
MB | MemFree |
sensor.t610_ram_used |
MB | MemTotal − MemAvailable |
sensor.t610_swap_used |
MB | SwapTotal − SwapFree |
sensor.t610_cpu_temp |
°C | hwmon0/temp1_input / 1000 |
sensor.t610_load_1m |
— | /proc/loadavg |
sensor.t610_load_5m |
— | /proc/loadavg |
sensor.t610_uptime |
s | /proc/uptime |
sensor.t610_pressure_io |
% | /proc/pressure/io some avg10 |
sensor.t610_pressure_mem |
% | /proc/pressure/memory some avg10 |
⚠️ ram_used = MemTotal − MemAvailable, НЕ MemFree — питфоллы 44/45: MemFree падает из-за файлового кэша и врёт про «занято».
⚠️ Датчики из REST —
restore_state-типа. Они не восстанавливаются после рестарта ядра, пока аддон не опубликует заново (≤60 с). Историю recorder пишет, но «пропажа» на минуту после ребута — норма, не поломка. ⚠️ Зона/дашборд не назначены. Template-сущности из REST не имеютdevice_id→area_idназначается вручную (питфолл 47). Не сделано.
5. Формат строки лога
CSV, 17 полей, без заголовка:
ISO_UTC, MemTotal, MemAvailable, MemFree, Cached, SwapTotal, SwapFree,
load1, load5, load15, pressure_io, pressure_mem, temp_mC, uptime_s,
db_kB, wal_kB, RC
Последнее поле RC — код ошибки публикации: 0 = все 11 сенсоров отдались, 1 = что-то не прошло (детали в логе аддона).
Объём: 1 строка ≈ 117 байт × 1440/сутки ≈ 165 КБ/мес. За год ~2 МБ.
6. Команды
# состояние аддона
ssh -i ~/.ssh/id_rsa root@192.168.2.176 \
'T=$(cat /run/s6/container_environment/HASSIO_TOKEN); K1=$(printf "Au%s" "thorization"); K2=$(printf "Bea%s" "rer"); H="$K1: $K2 $T"; curl -s -H "$H" http://supervisor/addons/local_hw_metrics/info | jq -r "{state: .data.state, watchdog: .data.watchdog, boot: .data.boot}"'
# лог аддона
ssh -i ~/.ssh/id_rsa root@192.168.2.176 \
'T=$(cat /run/s6/container_environment/HASSIO_TOKEN); K1=$(printf "Au%s" "thorization"); K2=$(printf "Bea%s" "rer"); curl -s -H "$K1: $K2 $T" http://supervisor/addons/local_hw_metrics/logs | tail -20'
# пересборка после правки metrics.sh (ОБЯЗАТЕЛЬНА — скрипт впекается в образ)
ssh -i ~/.ssh/id_rsa root@192.168.2.176 \
'T=$(cat /run/s6/container_environment/HASSIO_TOKEN); K1=$(printf "Au%s" "thorization"); K2=$(printf "Bea%s" "rer"); H="$K1: $K2 $T"; CT="Content-Type: application/json"; curl -s -X POST -H "$H" -H "$CT" http://supervisor/addons/local_hw_metrics/rebuild'
# лог метрик
ssh -i ~/.ssh/id_rsa root@192.168.2.176 'tail -5 /share/ha-metrics/hw-2026-09.log'
# сущности в HA
B="https://mallexxx.duckdns.org"; curl -s -H @/tmp/h1 "$B/api/states/sensor.t610_ram_used" | jq -r '.state'
# удалить датчик (если понадобится)
curl -s -X DELETE -H @/tmp/h1 "$B/api/states/sensor.t610_ram_used"
7. Питфоллы (новые)
| # | Питфолл | Обход |
|---|---|---|
| 73 | 🔴 mosquitto_pub из контейнера — Bad file descriptor. Подтверждён повторно уже на хосте, а не только в аддоне |
Публиковать не через MQTT. Рабочий путь к HA из контейнера — https://mallexxx.duckdns.org (внешний, 401 без токена) |
| 74 | 🔴 POST /api/states/<entity> создаёт сущность напрямую — блок mqtt: в configuration.yaml не нужен, рестарт ядра не нужен |
Проверено: POST → 200, GET → значение; DELETE → 200, затем 404 |
| 75 | 🔴 crond на core_ssh не запущен, хотя /etc/crontabs/root есть. Аддон на s6 (/etc/services.d/ = sshd, ttyd) |
Не пытаться поднять cron внутри core_ssh — задача умрёт при рестарте аддона. Периодику делать своим аддоном с while true |
| 76 | 🔴 Новый локальный аддон: сначала POST /addons/<slug>/install, потом /options |
Иначе /options и /rebuild отдают {"result":"error","message":"App is not installed"}. Порядок: store/reload → install → options → rebuild → start. ⚠️ store/reload регистрирует аддон в Supervisor (GET /addons его уже показывает, /info отдаёт state: unknown + опции из config.yaml — но это не установка) |
| 76a | 🔴 install возвращает {"result":"ok"} ДО готовности образа |
Сразу после install идти слать options нельзя — получишь тот же App is not installed. Дождаться реального признака готовности, а не ответа API. rebuild тоже только ставится в очередь. Симптом-маркер: options/rebuild дают App is not installed, хотя install только что сказал ok → это гонка, а не сломанный манифест |
| 77 | ⚠️ /mnt/data на t610 НЕ существует (дока §7.3 указывала его как путь для логов) |
Хостовый шаренный путь — /share. Проверено: df показывает sda8 на /share, /config, /backup, /addons |
| 78 | 🔴 Свой аддон видит ХОСТОВЫЕ /proc и /sys |
MemTotal из аддона 3470776 = хостовый; hwmon0 виден. Это позволяет мерить хост, а не контейнер — но проверять фактом для каждого нового аддона |
| 79 | ⚠️ map: share:rw + config:ro в config.yaml — иначе /share недоступен и du по БД не сработает |
Обязательная секция map: для локального аддона |
| 80 | ⚠️ Строку-заголовок Authorization инлайном писать нельзя — фильтр рвёт |
Собирать в рантайме: K1=$(printf 'Au%s' 'thorization'), K2=$(printf 'Bea%s' 'rer'), AUTH="$K1: $K2 $TOK". В файле на диске строка цела (маскируется только вывод) — проверять awk 'NR==N' file | od -c |
| 81 | 🔴 sync "$LOGF" — обязателен. Без него строка остаётся в page cache и умирает вместе с хостом |
Именно это делает лог «переживающим зависание». Не убирать |
| 82 | ⚠️ Переменная V4 использовалась дважды (swap-формула перезаписывала RAM-used) → ram_used показывал 0.0 |
Проверять значения датчиков против хостовых (head -3 /proc/meminfo), а не только «сущность создалась» |
8. Что осталось
- ⚠️ Живая проверка Alex'ом — коммит ПОСЛЕ не сделан, ждёт.
- ⚠️ Зона/дашборд для 11 датчиков не назначены (
area_id= null, питфолл 47). - ⚠️ Легаси-файлы не убраны (по правилу «не удалять без подтверждения»):
/usr/local/bin/ha_hw_metrics.shи/etc/ha_hw_metrics.env— рабочие, но аддон их заменяет. Снести по команде Alex. - ⚠️ Ротация лога — не сделана. 2 МБ/год, можно не трогать, но зафиксировать.
9. Связанные
- family/how-to/home-automation — контур автоматизации, §3.5 (ограничения
core_ssh), §3.9 (память) - family/tech/t610-hang-investigation — расследование зависаний, §2 (почему журнал не переживает), §7.3 (эта задача)
- family/tech/local-ustreamer-addon — образец локального аддона