393 lines
18 KiB
Markdown
393 lines
18 KiB
Markdown
# ZONT Prometheus Exporter
|
||
|
||
Экспортер телеметрии отопительных контроллеров **ZONT** (H-1V, H-1V.02, H1V02_PRO, H-1000, H-2000 и др.) в формате Prometheus.
|
||
|
||
Собирает текущее состояние устройства через облачное API `my.zont.online` и отдаёт метрики на HTTP-эндпоинт `/metrics`. Подходит для мониторинга в Grafana, алертинга в Alertmanager и любого стека, совместимого с Prometheus.
|
||
|
||
---
|
||
|
||
## 📋 Содержание
|
||
|
||
- [Возможности](#-возможности)
|
||
- [Требования](#-требования)
|
||
- [Быстрый старт](#-быстрый-старт)
|
||
- [Переменные окружения](#-переменные-окружения)
|
||
- [Метрики](#-метрики)
|
||
- [Интеграция с Prometheus](#-интеграция-с-prometheus)
|
||
- [Пример алертов](#-пример-алертов)
|
||
- [Диагностика](#-диагностика)
|
||
- [Ограничения](#-ограничения)
|
||
- [Безопасность](#-безопасность)
|
||
- [Лицензия](#-лицензия)
|
||
|
||
---
|
||
|
||
## 🚀 Возможности
|
||
|
||
- **Текущее состояние контроллера**: онлайн-статус, напряжение питания, память, источник питания.
|
||
- **Связь**: уровень GSM, состояние регистрации в сети, RSSI Wi-Fi, наличие связи с сервером ZONT.
|
||
- **Температуры**: проводные и аналоговые датчики (в т.ч. «Погода из интернета»), статус исправности.
|
||
- **Отопление**: целевая и заданная температура по каждому отопительному контуру, время работы.
|
||
- **Котёл / OpenTherm**: расчётная температура теплоносителя, уставка ГВС, наличие связи с котлом, аварии.
|
||
- **Погода**: температура с внешнего источника.
|
||
- **Аналоговые входы**: напряжение на входах (например, контроль питания).
|
||
|
||
Работает как отдельный контейнер, не требует установки чего-либо на хост.
|
||
|
||
---
|
||
|
||
## ✅ Требования
|
||
|
||
- Docker (или Docker Compose)
|
||
- Аккаунт в личном кабинете [my.zont.online](https://my.zont.online) с добавленным устройством ZONT
|
||
- Сетевая доступность `my.zont.online:443` из контейнера
|
||
- Prometheus (опционально) для сбора метрик
|
||
|
||
---
|
||
|
||
## 🏁 Быстрый старт
|
||
|
||
### 1. Создайте файлы
|
||
|
||
Вам нужны два файла:
|
||
|
||
- `zont_exporter.py` — код экспортера
|
||
- `Dockerfile` — сборка образа
|
||
|
||
### 2. Соберите образ
|
||
|
||
```bash
|
||
docker build -t zont-exporter .
|
||
```
|
||
|
||
### 3. Запустите контейнер
|
||
|
||
```bash
|
||
docker run -d \
|
||
--name zont-exporter \
|
||
--restart unless-stopped \
|
||
-p 9101:9101 \
|
||
-e ZONT_LOGIN='your_login' \
|
||
-e ZONT_PASSWORD='your_password' \
|
||
-e ZONT_DEVICE_ID='554007' \
|
||
-e ZONT_CLIENT='you@example.com' \
|
||
zont-exporter
|
||
```
|
||
|
||
### 4. Проверьте
|
||
|
||
```bash
|
||
curl http://localhost:9101/metrics | grep zont_
|
||
```
|
||
|
||
Вы должны увидеть метрики вида:
|
||
|
||
```
|
||
zont_device_online{device_id="554007"} 1.0
|
||
zont_voltage_volts{device_id="554007"} 12.1
|
||
zont_gsm_level{device_id="554007"} 17.0
|
||
zont_temperature_celsius{device_id="554007",name="Температура гаража 150",object_id="8200"} 18.8
|
||
zont_boiler_connected{adapter_id="4096",device_id="554007"} 0.0
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 Переменные окружения
|
||
|
||
| Переменная | Обязательна | По умолчанию | Описание |
|
||
|---|---|---|---|
|
||
| `ZONT_LOGIN` | ✅ | — | Логин от личного кабинета `my.zont.online` |
|
||
| `ZONT_PASSWORD` | ✅ | — | Пароль от личного кабинета |
|
||
| `ZONT_DEVICE_ID` | ✅ | — | Числовой ID устройства (см. ниже, как узнать) |
|
||
| `ZONT_CLIENT` | ❌ | `my_exporter@example.com` | Контактный email для заголовка `X-ZONT-Client`. ZONT использует его для связи при изменениях API. Укажите свой. |
|
||
| `SCRAPE_INTERVAL` | ❌ | `60` | Интервал сбора метрик, секунды. Не имеет смысла ставить меньше 30 — API не успевает обновлять данные. |
|
||
| `EXPORTER_PORT` | ❌ | `9101` | Порт, на котором экспортер слушает `/metrics` внутри контейнера |
|
||
| `LOG_LEVEL` | ❌ | `INFO` | Уровень логирования: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`. Регистр не важен. |
|
||
|
||
### Как узнать `ZONT_DEVICE_ID`
|
||
|
||
**Способ 1. Через API (рекомендуется)**
|
||
|
||
```bash
|
||
curl -s -X POST 'https://my.zont.online/api/devices' \
|
||
-u 'login:password' \
|
||
-H 'X-ZONT-Client: you@example.com' \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{"load_io": true}' | python3 -m json.tool | grep -E '"id"|"name"|"serial"'
|
||
```
|
||
|
||
В ответе найдите блок своего устройства — поле `"id"` и есть `ZONT_DEVICE_ID`.
|
||
|
||
**Способ 2. Через личный кабинет**
|
||
|
||
Откройте устройство в веб-интерфейсе `my.zont.online` — ID обычно видно в URL страницы устройства.
|
||
|
||
### Пример запуска с нестандартным портом и отладкой
|
||
|
||
```bash
|
||
docker run -d \
|
||
--name zont-exporter \
|
||
--restart unless-stopped \
|
||
-p 9200:9200 \
|
||
-e ZONT_LOGIN='your_login' \
|
||
-e ZONT_PASSWORD='your_password' \
|
||
-e ZONT_DEVICE_ID='554007' \
|
||
-e ZONT_CLIENT='you@example.com' \
|
||
-e EXPORTER_PORT='9200' \
|
||
-e LOG_LEVEL='DEBUG' \
|
||
zont-exporter
|
||
```
|
||
|
||
> ⚠️ Номера портов в `-p <хост>:<контейнер>` и `EXPORTER_PORT` должны совпадать по контейнерной части. Пример выше: `EXPORTER_PORT=9200` и `-p 9200:9200`. Если хост-порт занят — используйте `-p 8080:9200`.
|
||
|
||
---
|
||
|
||
## 📊 Метрики
|
||
|
||
Все метрики имеют префикс `zont_` и label `device_id`. Для удобства читаемые имена объектов (датчиков, контуров) добавляются как label `name`.
|
||
|
||
### Общие
|
||
|
||
| Метрика | Тип | Labels | Описание |
|
||
|---|---|---|---|
|
||
| `zont_device_online` | gauge | `device_id` | 1 — устройство на связи, 0 — offline |
|
||
| `zont_device_last_receive_time` | gauge | `device_id` | Unix-время последнего пакета от устройства |
|
||
| `zont_voltage_volts` | gauge | `device_id` | Напряжение основного питания, В |
|
||
| `zont_power_source` | gauge | `device_id` | 1 — основное питание, 0 — иное |
|
||
| `zont_memory_used_percent` | gauge | `device_id` | Использование памяти контроллера, % |
|
||
| `zont_memory_used_bytes` | gauge | `device_id` | Использование памяти, байт |
|
||
| `zont_internet_weather_celsius` | gauge | `device_id` | Температура с внешнего источника погоды, °C |
|
||
|
||
### Связь
|
||
|
||
| Метрика | Тип | Labels | Описание |
|
||
|---|---|---|---|
|
||
| `zont_gsm_level` | gauge | `device_id` | Уровень сигнала GSM, 0–31 |
|
||
| `zont_gsm_state` | gauge | `device_id` | 0=not-registered, 1=home-network, 2=searching, 3=rejected, 5=roaming |
|
||
| `zont_wifi_rssi` | gauge | `device_id` | Уровень сигнала Wi-Fi |
|
||
| `zont_server_connected` | gauge | `device_id` | 1 — есть связь с сервером ZONT |
|
||
|
||
### Датчики температуры и аналоговые входы
|
||
|
||
| Метрика | Тип | Labels | Описание |
|
||
|---|---|---|---|
|
||
| `zont_temperature_celsius` | gauge | `device_id`, `object_id`, `name` | Текущая температура, °C |
|
||
| `zont_temperature_sensor_ok` | gauge | `device_id`, `object_id`, `name` | 1 — датчик исправен |
|
||
| `zont_analog_voltage_volts` | gauge | `device_id`, `object_id`, `name` | Напряжение на аналоговом входе, В |
|
||
|
||
### Отопление
|
||
|
||
| Метрика | Тип | Labels | Описание |
|
||
|---|---|---|---|
|
||
| `zont_heating_target_temp_celsius` | gauge | `device_id`, `circuit_id`, `name` | Фактическая целевая температура контура |
|
||
| `zont_heating_setpoint_temp_celsius` | gauge | `device_id`, `circuit_id`, `name` | Заданная температура (уставка) |
|
||
| `zont_heating_worktime_seconds` | gauge | `device_id`, `circuit_id`, `name` | Время работы контура за последнюю минуту, сек |
|
||
| `zont_heating_status` | gauge | `device_id`, `circuit_id`, `name` | Код статуса контура |
|
||
|
||
### Котёл / OpenTherm
|
||
|
||
| Метрика | Тип | Labels | Описание |
|
||
|---|---|---|---|
|
||
| `zont_boiler_cs_celsius` | gauge | `device_id`, `adapter_id` | Расчётная температура теплоносителя, °C |
|
||
| `zont_boiler_ds_celsius` | gauge | `device_id`, `adapter_id` | Уставка температуры ГВС, °C |
|
||
| `zont_boiler_connected` | gauge | `device_id`, `adapter_id` | 1 — котёл на связи, 0 — нет |
|
||
| `zont_boiler_fail` | gauge | `device_id`, `adapter_id` | 1 — авария котла |
|
||
|
||
---
|
||
|
||
## 🔌 Интеграция с Prometheus
|
||
|
||
Добавьте в `prometheus.yml`:
|
||
|
||
```yaml
|
||
scrape_configs:
|
||
- job_name: 'zont'
|
||
scrape_interval: 60s
|
||
scrape_timeout: 20s
|
||
static_configs:
|
||
- targets: ['zont-exporter:9101']
|
||
labels:
|
||
instance: 'home-boiler'
|
||
```
|
||
|
||
Если Prometheus работает в том же Docker-сети, используйте имя контейнера. Если на хосте — `host.docker.internal:9101` (Windows/macOS) или IP хоста (Linux).
|
||
|
||
### Docker Compose (пример)
|
||
|
||
```yaml
|
||
services:
|
||
zont-exporter:
|
||
build: .
|
||
container_name: zont-exporter
|
||
restart: unless-stopped
|
||
ports:
|
||
- "9101:9101"
|
||
environment:
|
||
ZONT_LOGIN: "your_login"
|
||
ZONT_PASSWORD: "your_password"
|
||
ZONT_DEVICE_ID: "554007"
|
||
ZONT_CLIENT: "you@example.com"
|
||
SCRAPE_INTERVAL: "60"
|
||
EXPORTER_PORT: "9101"
|
||
LOG_LEVEL: "INFO"
|
||
```
|
||
|
||
---
|
||
|
||
## 🚨 Пример алертов
|
||
|
||
`prometheus/rules/zont.yml`:
|
||
|
||
```yaml
|
||
groups:
|
||
- name: zont
|
||
rules:
|
||
- alert: ZontDeviceOffline
|
||
expr: zont_device_online == 0
|
||
for: 10m
|
||
labels:
|
||
severity: critical
|
||
annotations:
|
||
summary: "ZONT {{ $labels.device_id }} offline"
|
||
description: "Устройство не выходит на связь более 10 минут."
|
||
|
||
- alert: ZontServerDisconnected
|
||
expr: zont_server_connected == 0
|
||
for: 5m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "ZONT {{ $labels.device_id }} потерял связь с сервером"
|
||
|
||
- alert: ZontBoilerDisconnected
|
||
expr: zont_boiler_connected == 0
|
||
for: 15m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "ZONT {{ $labels.device_id }}: нет связи с котлом (адаптер {{ $labels.adapter_id }})"
|
||
|
||
- alert: ZontBoilerFail
|
||
expr: zont_boiler_fail == 1
|
||
for: 5m
|
||
labels:
|
||
severity: critical
|
||
annotations:
|
||
summary: "ZONT {{ $labels.device_id }}: авария котла"
|
||
|
||
- alert: ZontLowVoltage
|
||
expr: zont_voltage_volts < 10
|
||
for: 5m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "ZONT {{ $labels.device_id }}: низкое напряжение питания {{ $value }} В"
|
||
|
||
- alert: ZontWeakGSM
|
||
expr: zont_gsm_level < 5
|
||
for: 15m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "ZONT {{ $labels.device_id }}: слабый сигнал GSM ({{ $value }})"
|
||
|
||
- alert: ZontHighMemory
|
||
expr: zont_memory_used_percent > 85
|
||
for: 30m
|
||
labels:
|
||
severity: info
|
||
annotations:
|
||
summary: "ZONT {{ $labels.device_id }}: высокая загрузка памяти {{ $value }}%"
|
||
```
|
||
|
||
---
|
||
|
||
## 🩺 Диагностика
|
||
|
||
### Метрики пустые или `/metrics` отдаёт только `python_*`
|
||
|
||
1. Проверьте логи:
|
||
```bash
|
||
docker logs zont-exporter --tail 100
|
||
```
|
||
2. Если непонятно, что происходит, включите подробное логирование:
|
||
```bash
|
||
docker run -d --name zont-exporter \
|
||
-p 9101:9101 \
|
||
-e ZONT_LOGIN='...' -e ZONT_PASSWORD='...' \
|
||
-e ZONT_DEVICE_ID='554007' \
|
||
-e ZONT_CLIENT='you@example.com' \
|
||
-e LOG_LEVEL='DEBUG' \
|
||
zont-exporter
|
||
```
|
||
Затем смотрите логи:
|
||
```bash
|
||
docker logs zont-exporter --tail 200
|
||
```
|
||
В режиме `DEBUG` видны детали ответов API и структура `io`-объектов.
|
||
|
||
3. Ищите в логах строки:
|
||
- `Successfully obtained ZONT auth token.` — токен получен.
|
||
- `Failed to get auth token: ...` — проблема с логином/паролем.
|
||
- `API error: ...` — API вернул ошибку.
|
||
- `Device 554007 not found in account.` — неверный `ZONT_DEVICE_ID`.
|
||
- `Metrics collected successfully for device ...` — всё ок.
|
||
|
||
### Ошибка `SyntaxError: name 'auth_token' is used prior to global declaration`
|
||
|
||
Объявление `global auth_token` должно стоять **в самом начале** функции, до любого использования переменной. В актуальной версии кода это уже учтено.
|
||
|
||
### Ошибка аутентификации
|
||
|
||
Проверьте логин/пароль руками:
|
||
|
||
```bash
|
||
curl -s -X POST 'https://my.zont.online/api/get_authtoken' \
|
||
-u 'login:password' \
|
||
-H 'X-ZONT-Client: you@example.com' \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{"client_name": "debug"}' | python3 -m json.tool
|
||
```
|
||
|
||
Ожидаемый ответ: `{"ok": true, "token": "..."}`.
|
||
|
||
### Хочу больше данных (история, а не текущее состояние)
|
||
|
||
Метод `devices` отдаёт **снимок текущего состояния**. Историю (графики за период) можно получить через метод `load_data` с типами `z3k_temperature`, `z3k_heating_circuit`, `z3k_boiler_adapter`, `ztc_state`. Для этого требуется отдельная логика — при необходимости расширьте экспортер.
|
||
|
||
---
|
||
|
||
## ⚠️ Ограничения
|
||
|
||
- Экспортер собирает **только текущее состояние**, не историю. Для графиков используйте `load_data` (см. выше) или смотрите графики в личном кабинете ZONT.
|
||
- Работает только с **устройствами, привязанными к аккаунту my.zont.online**. Локальный доступ к контроллеру по USB/локальной сети не используется.
|
||
- Тип `thermostat_work` через `load_data` для модели **H1V02_PRO** (H-1V.02) возвращает пустые массивы. Используйте данные из `devices?load_io=true` — там вся актуальная телеметрия.
|
||
- Набор доступных полей в `io.z3k-state` зависит от модели, прошивки и конфигурации. Экспортер игнорирует отсутствующие поля — это нормально.
|
||
- Требуется HTTPS-доступ к `my.zont.online`. При использовании прокси задайте `HTTPS_PROXY` в окружении контейнера.
|
||
|
||
---
|
||
|
||
## 🔒 Безопасность
|
||
|
||
- **Не храните логин/пароль в открытом виде в Docker Compose / systemd unit.** Используйте:
|
||
- Docker secrets,
|
||
- переменные окружения из защищённого файла (`--env-file`),
|
||
- внешний секрет-менеджер (Vault, SOPS, Ansible Vault).
|
||
- **Заведите отдельного пользователя** в ZONT для API-интеграции. Не используйте основную учётную запись владельца.
|
||
- **Ротируйте пароль**, если он где-то засветился (переписка, скриншоты, git-история).
|
||
- Экспортер **не пишет** логин/пароль в логи, но может логировать ответ API. Проверьте уровень логирования, если ответы содержат чувствительные данные.
|
||
- Токен ZONT (`X-ZONT-Token`) хранится только в памяти процесса и перезапрашивается при истечении (`unauthorized` / `token_expired`).
|
||
- Эндпоинт `/metrics` **не имеет аутентификации**. Не выставляйте его в интернет. Используйте network policy / firewall.
|
||
|
||
---
|
||
|
||
## 📄 Лицензия
|
||
|
||
MIT. Используйте на свой риск. Проект не связан с ООО «Микро Лайн» / ZONT — это независимая интеграция с публичным API.
|
||
|
||
---
|
||
|
||
## 🙏 Благодарности
|
||
|
||
- Команде ZONT за публичное API и [документацию](https://my.zont.online/api).
|
||
- Сообществу Prometheus за `prometheus_client`. |