# 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`.