Files
obsidian-vault/family/tech/t610-hw-metrics-addon.md
T

23 KiB
Raw Blame History

aliases, created, namespace, related, tags, title, type, updated
aliases created namespace related tags title type updated
t610 metrics
hw_metrics addon
метрики t610
RAM температура датчики HA
лог переживающий зависание
2026-09-17 family
family/how-to/home-automation
family/tech/t610-hang-investigation
family/tech/local-ustreamer-addon
family
tech
smarthome
t610
monitoring
📊 t610 — метрики хоста (RAM, температура) + лог, переживающий зависание tech 2026-09-17b

📊 t610 — метрики хоста (RAM, температура) + лог, переживающий зависание

Статус: РАБОТАЕТ (2026-09-17). Локальный аддон local_hw_metrics v8.0.0, интервал 60 с, MQTT discovery. 11 датчиков живые в HA, лог пишется на диск со sync. ⚠️ Известный дефект: entity_id с двойным префиксом sensor.t610_t610_* (не переименовывается, §8). Родительские доки: family/how-to/home-automation · расследование зависаний — family/tech/t610-hang-investigation


1. Зачем

Две задачи, поставленные Alex 2026-09-17:

  1. Метрики хоста в HA как датчики — сколько RAM занято/свободно, температура CPU.
  2. Лог, который переживает зависание. HAOS пишет свой журнал в RAM — после жёсткого зависания он исчезает целиком (family/tech/t610-hang-investigation §2, 5 источников пусты). Каждый инцидент стирал свои доказательства. Нужно было, чтобы следующий случай оставил следы.

🔴 Требование Alex к сдаче работы: «Я готов результат проверь когда датчики будут в ha ui». То есть «сущность отвечает по API» — не приёмка. Датчики должны быть видны в UI, с зоной и дашбордом. Это меняет план: назначение зоны/дашборда — часть «готово», а не «потом».


2. Архитектура (ФИНАЛЬНАЯ — MQTT discovery)

аддон local_hw_metrics v8 (alpine, bash, while true)
   │  каждые 60 с
   ├─ читает ХОСТОВЫЕ /proc/meminfo, /proc/loadavg, /proc/pressure/*,
   │           /sys/class/hwmon/hwmon0/temp1_input, /proc/uptime,
   │           размер /config/home-assistant-v2.db + -wal
   ├─ один retained JSON в топик t610/hw/state
   │  + retained "online" в t610/hw/available
   │  + retained discovery-конфиги homeassistant/sensor/t610_host/<key>/config
   │            │
   │            └──► HA (интеграция mqtt) САМ создаёт 11 настоящих сущностей
   │                 в entity_registry, с unique_id и device `t610`
   └─ дописывает строку в /share/ha-metrics/hw-YYYY-MM.log + sync
                              ▲
                              └─ переживает жёсткое зависание

Почему MQTT discovery, а НЕ REST POST /api/states

Это главное решение сессии, и оно переигрывалось дважды.

REST POST /api/states MQTT discovery
Попадает в entity_registry нет да
unique_id нет есть
device нет есть (t610)
area_id назначить нельзя (нет записи в реестре) можно
Видно на дашбордах зон нет да
История в recorder пишется, но к несуществующей записи нормально

🔴 Урок: REST-сущности живут только в state machine, их нет в реестре. Проверено фактом: POST /api/states/sensor.x → сущность отвечает по API, но config/entity_registry/list её не содержит (find t610 → 0 записей). Для датчиков, которые нужны на дашборде, REST-путь негоден — только MQTT discovery или configuration.yaml.

Почему аддон, а не 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).

Как выглядит публикация (ключевой фрагмент)

# один retained JSON со всеми значениями
PUB=... mosquitto_pub -h core-mosquitto -p 1883 -u "$MQ_USER" -P "$MQ_PASS" \
     -t "t610/hw/state" -m "$PAYLOAD" -r

# discovery-конфиг одного сенсора (retained)
CFG='{"name":"RAM total","unique_id":"t610hw_mem_total","state_topic":"t610/hw/state",
      "value_template":"{{ value_json.mem_total }}","availability_topic":"t610/hw/available",
      "payload_available":"online","payload_not_available":"offline",
      "unit_of_measurement":"MB","device_class":"data_size","state_class":"measurement",
      "device":{"identifiers":["t610_host"],"name":"t610","manufacturer":"HP","model":"t610"}}'
mosquitto_pub ... -t "homeassistant/sensor/t610_host/mem_total/config" -m "$CFG" -r

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.envMQ_USER/MQ_PASS/TOK, chmod 600. Оставлен от REST-попытки, не используется аддоном
Скрипт на хосте (легаси) /usr/local/bin/ha_hw_metrics.sh — v2 (REST). Рабочий, но аддон его заменяет

config.yaml аддона (v8, финал)

name: "HW metrics (t610: RAM + temp)"
version: "8.0.0"
slug: "hw_metrics"
arch: [amd64, aarch64]
startup: services
boot: auto          # стартует при загрузке ХОСТА
init: false
host_network: true  # ← нужно: даёт доступ к Docker DNS (core-mosquitto) и хостовому /proc
map:
  - share:rw        # ← обязательно: /share/ha-metrics для лога
  - config:ro       # ← обязательно: du по home-assistant_v2.db
options:
  interval: 60
  mqtt_host: "core-mosquitto"
  mqtt_port: 1883
  mqtt_user: "zont"
  mqtt_password: ""
schema:
  interval: "int(10,600)"
  mqtt_host: "str"
  mqtt_port: "port"
  mqtt_user: "str"
  mqtt_password: "password"

Dockerfile: FROM alpine:3.20 + apk add bash jq mosquitto-clients coreutils, CMD ["/bin/bash","/metrics.sh"].


4. Датчики (11) — фактические ID

🔴 entity_id получились с ДВОЙНЫМ префиксомsensor.t610_t610_*. Причина и почему не исправлено — §8. В UI видно корректно (t610 RAM total), кривой только entity_id для автоматизаций/шаблонов.

entity_id (факт) Отображаемое имя Единица Источник
sensor.t610_t610_ram_total t610 RAM total MB MemTotal
sensor.t610_t610_ram_available t610 RAM available MB MemAvailable
sensor.t610_t610_ram_free t610 RAM free MB MemFree
sensor.t610_t610_ram_used t610 RAM used MB MemTotal MemAvailable
sensor.t610_t610_swap_used t610 SWAP used MB SwapTotal SwapFree
sensor.t610_t610_cpu_temp t610 CPU temp °C hwmon0/temp1_input / 1000
sensor.t610_t610_load_1m t610 load 1m /proc/loadavg
sensor.t610_t610_load_5m t610 load 5m /proc/loadavg
sensor.t610_t610_uptime t610 uptime s /proc/uptime
sensor.t610_t610_pressure_io t610 pressure io % /proc/pressure/io some avg10
sensor.t610_t610_pressure_mem t610 pressure mem % /proc/pressure/memory some avg10

⚠️ ram_used = MemTotal MemAvailable, НЕ MemFree — питфоллы 44/45: MemFree падает из-за файлового кэша и врёт про «занято».

Проверено совпадением с хостом: ram_total 3389.4 MB = 3470776 kB ✓ · cpu_temp 61.8 °C = 61750 мК ✓ · ram_used 832.8 MB = (34707762618024)/1024


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 = всё отдалось, 1 = что-то не прошло (детали в логе аддона). Фактический пример живой строки:

2026-09-17T07:25:57Z,3470776,2618024,778160,1723516,1145356,1145356,1.36,1.18,1.00,0.95,0.00,61750,4864,79616,12,0

Объём: 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, version: .data.version}"'

# лог аддона (живой, не буфер)
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'

# живые значения датчиков
B="https://mallexxx.duckdns.org"; curl -s -H @/tmp/h1 "$B/api/states" | jq -r '.[] | select(.entity_id|test("t610.*(ram|swap|cpu|load|uptime|pressure)")) | "\(.entity_id) = \(.state) \(.attributes.unit_of_measurement // "")"'

# проверка «в реестре ли сущности» (ключевой критерий для дашборда!)
cd ~/tmp-t610 && python3 chk_reg.py

# МОСТИК: сброс retained discovery-топиков (лечит зависшие/дублирующиеся сущности)
scp -i ~/.ssh/id_rsa clear_t610_topics.sh root@192.168.2.176:/tmp/
ssh -i ~/.ssh/id_rsa root@192.168.2.176 'bash /tmp/clear_t610_topics.sh'

🔴 Порядок установки/обновления локального аддона (стоил 8 итераций)

# 1) store/reload — регистрирует аддон в Supervisor (ещё НЕ установка)
# 2) install    — возвращает ok ДО готовности → НЕ слать options сразу
# 3) options    — задать MQTT-пароль
# 4) rebuild    — только ставится в очередь
# 5) start

После правки config.yaml (схемы/опций) Supervisor кеширует старый манифестstore/reload схему не обновляет. Помогает uninstallstore/reloadinstall и поднятие version в config.yaml.


7. Питфоллы

# Питфолл Обход
73 🔴 mosquitto_pub из контейнера падает Bad file descriptor — НО только на неверном адресе. Работает на core-mosquitto (Docker DNS, IPv6 fd0c:ac1e:2100::7), падает на 127.0.0.1 и 192.168.2.176 (свой netns, 1883 не слушается) Прежняя формулировка «MQTT из контейнера не работает» — НЕВЕРНА. Адресовать core-mosquitto. Проверка: getent hosts core-mosquitto + nc -z core-mosquitto 1883
74 🔴 POST /api/states/<entity> создаёт сущность, которой НЕТ в entity_registry Для датчиков, которые нужны в UI/зоне/дашборде, REST негоден — только MQTT discovery или configuration.yaml. REST-путь годится для разовых записей без UI
75 🔴 crond на core_ssh не запущен, хотя /etc/crontabs/root есть. Аддон на s6 (/etc/services.d/ = sshd, ttyd) Периодику делать своим аддоном с while true
76 🔴 Новый локальный аддон: install ДО options/rebuild Иначе {"result":"error","message":"App is not installed"}. Порядок в §6. ⚠️ install возвращает ok до готовности — сразу слать options нельзя, это гонка
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 по БД не сработает
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), а не только «сущность создалась»
83 🔴 object_id в discovery-конфиге + кириллица в device.name = транслитерированный entity_id (sensor.hp_t610_khost_ha_t610_ram_vsego) device.nameтолько латиницей; object_id не задавать. Тогда entity_id строится из device name + name
84 🔴 id_reuse: Identifier values have to increase запирает entity_registry/update и /remove — на переименование, name, area_id, удаление, даже на смену unique_id. Промежуточные имена, name_by_user, удаление device, рестарт ядра — НЕ помогают ⚠️ Не тратить время на переименование. Радикальный путь — новый unique_id (t610hw_*), тогда HA создаёт сущности заново. Но даже это не спасло от двойного префикса (§8)
85 🔴 Сброс retained discovery-топиков (-m "" -r) удаляет сущности из HA — и снимает блокировку id_reuse Это штатный способ «пересоздать» MQTT-сущности: очистить топики → HA удалит сущности → переопубликовать конфиг. mosquitto_pub -t <topic>/config -m "" -r
86 ⚠️ GET /addons/<slug>/info отдаёт state: unknown после uninstall — это норма Не считать поломкой

8. 🔴 Известный дефект: двойной префикс entity_id

Симптом: sensor.t610_t610_ram_total вместо sensor.t610_ram_total. Причина: HA строит entity_id как <device.name> + "_" + <entity.name>. Device — t610, имя сущности в первой редакции было t610 RAM total → склейка дала двойной префикс. Почему не исправлено: после первого создания entity_id зафиксировался в реестре, а любая операция entity_registry/update / /remove с этими сущностями отбивается id_reuse: Identifier values have to increase (питфолл 84).

Что пробовалось (всё безрезультатно):

  1. Промежуточное имя sensor.zz_tmp_*id_reuse.
  2. name_by_user через config/entity_registry/update с полем name → прошло 1 из 11, остальные id_reuse.
  3. Удаление device из реестра → id_reuse.
  4. Смена unique_id (t610_*t610hw_*) + сброс discovery-топиков → HA создал сущности заново, но entity_id всё равно двойной (уже с name = t610 RAM total).
  5. Убирание object_id, латиница в device.nameне помогло (создались sensor.t610_t610_*).
  6. Рестарт HA Core (POST /api/services/homeassistant/restart) → блокировка переживает рестарт.

Оценка влияния: в UI всё корректно — показывается t610 RAM total (friendly_name), дашборд и зона работают. Страдают только автоматизации/шаблоны, где пишется полный entity_id.

Возможный фикс (не применён, ждёт решения Alex): сменить unique_id ещё раз (например на t610hwm_*) вместе с именем сущности без префикса (name: "RAM total"), предварительно очистив retained-топики, — тогда HA создаст сущности с нуля и entity_id выйдет sensor.t610_ram_total.


9. Что осталось

  • ⚠️ entity_id с двойным префиксом (§8) — ждёт решения Alex.
  • ⚠️ Зона/дашборд для 11 датчиковarea_id не назначен. ⚠️ Назначить через реестр нельзя (id_reuse, питфолл 84) → делать карточку на дашборд через entity_id, либо зону на device (config/device_registry/update) — не проверено.
  • ⚠️ Коммит ПОСЛЕ в ~/Automation/HA-ZONT-Modbus — ждёт проверки Alex'ом. Закоммичен только «коммит ДО»: 12ba22b «Sync configuration.yaml from t610 prod (kitchen hood fan template)».
  • ⚠️ Легаси-файлы /usr/local/bin/ha_hw_metrics.sh и /etc/ha_hw_metrics.env — рабочие, но аддон их заменяет. Снести по команде Alex.
  • ⚠️ Ротация лога не сделана: ~2 МБ/год, можно не трогать, но зафиксировать.
  • ⚠️ configuration.yaml НЕ менялся — блок mqtt: не добавлялся, интеграция mqtt уже была. Бэкап на t610 сделан вхолостую: /config/configuration.yaml.bak-hwmetrics-20260917-132251.

10. Побочный факт: MemTotal пойман как ступенька

Во время проверки (2026-09-17, ~13:11) снято с живого хоста: MemTotal = 3 470 776 kB = 3.31 ГБ, аптайм 6 минут — то есть хост только что перезагрузился, и память поднялась полным объёмом.

Это подтверждение уже описанного в family/tech/t610-hang-investigation механизма «ступенька между загрузками» (§3.1.2: два слота → ступенчатое падение объёма; §5.1 п.2: «если MemTotal меняется между загрузками → планка/слот нестабильны»), а не новое открытие. Раньше в доке был «живой факт 1.44 ГБ» — теперь есть точка 3.31 ГБ. Это ровно тот тип данных, который теперь фиксирует аддон — то, ради чего задача и делалась. Причину того ребута не выясняли.


11. Связанные