--- aliases: - t610 metrics - hw_metrics addon - метрики t610 - RAM температура датчики HA - лог переживающий зависание created: '2026-09-17' namespace: family related: - '[[family/how-to/home-automation]]' - '[[family/tech/t610-hang-investigation]]' - '[[family/tech/local-ustreamer-addon]]' tags: - family - tech - smarthome - t610 - monitoring title: "\U0001F4CA t610 — метрики хоста (RAM, температура) + лог, переживающий зависание" type: tech updated: '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//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`). ### Как выглядит публикация (ключевой фрагмент) ```bash # один 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.env` — `MQ_USER`/`MQ_PASS`/`TOK`, chmod 600. Оставлен от REST-попытки, **не используется аддоном** | | Скрипт на хосте (легаси) | `/usr/local/bin/ha_hw_metrics.sh` — v2 (REST). Рабочий, но аддон его заменяет | ### `config.yaml` аддона (v8, финал) ```yaml 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` = `(3470776−2618024)/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. Команды ```bash # состояние аддона 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 итераций) ```bash # 1) store/reload — регистрирует аддон в Supervisor (ещё НЕ установка) # 2) install — возвращает ok ДО готовности → НЕ слать options сразу # 3) options — задать MQTT-пароль # 4) rebuild — только ставится в очередь # 5) start ``` После правки `config.yaml` (схемы/опций) Supervisor **кеширует старый манифест** — `store/reload` схему не обновляет. Помогает **`uninstall` → `store/reload` → `install`** и **поднятие `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_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 /config -m "" -r` | | 86 | ⚠️ **`GET /addons//info` отдаёт `state: unknown` после `uninstall`** — это норма | Не считать поломкой | --- ## 8. 🔴 Известный дефект: двойной префикс `entity_id` **Симптом:** `sensor.t610_t610_ram_total` вместо `sensor.t610_ram_total`. **Причина:** HA строит `entity_id` как ` + "_" + `. 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. Связанные - [[family/how-to/home-automation]] — контур автоматизации, §3.5 (ограничения `core_ssh`), §3.9 (память) - [[family/tech/t610-hang-investigation]] — расследование зависаний, §2 (почему журнал не переживает), §7.3 (эта задача) - [[family/tech/local-ustreamer-addon]] — образец локального аддона