24 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-17b |
📊 t610 — метрики хоста (RAM, температура) + лог, переживающий зависание
Статус: ✅ РАБОТАЕТ (2026-09-17). Локальный аддон
local_hw_metricsv8.0.0, интервал 60 с, MQTT discovery. 11 датчиков живые в HA, лог пишется на диск соsync. ⚠️ Известный дефект:entity_idс двойным префиксомsensor.t610_t610_*(не переименовывается, §8). ⚠️ Зона/дашборд не назначены — и это блокирует приёмку: Alex принимает работу только когда датчики видны в HA UI (§1). Сессия 2026-09-17 остановлена на этой точке. Родительские доки: 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 источников пусты). Каждый инцидент стирал свои доказательства. Нужно было, чтобы следующий случай оставил следы.
🔴 Требование 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.env — MQ_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 = (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. Команды
# состояние аддона
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 схему не обновляет. Помогает 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> создаёт сущность, которой НЕТ в 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).
Что пробовалось (всё безрезультатно):
- Промежуточное имя
sensor.zz_tmp_*→id_reuse. name_by_userчерезconfig/entity_registry/updateс полемname→ прошло 1 из 11, остальныеid_reuse.- Удаление device из реестра →
id_reuse. - Смена
unique_id(t610_*→t610hw_*) + сброс discovery-топиков → HA создал сущности заново, ноentity_idвсё равно двойной (уже сname=t610 RAM total). - Убирание
object_id, латиница вdevice.name→ не помогло (создалисьsensor.t610_t610_*). - Рестарт 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. - 🔌 Смежная задача сессии: разбор «почему выключилось реле котла» →
family/tech/t610-relay-off-log-forensics. Вывод:
offв логбуке по Zigbee-реле — артефакт перезапуска, физического выключения не было. Появились питфоллы 87–92 (таймзона UTC↔+07,date -dна macOS, логбук вытесняется template-сводками, хардлайн-блок на словоrestartв командах).
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 — образец локального аддона