Add guard zones
Build and Push / maintence (push) Successful in 1s
Build and Push / buildApp (push) Successful in 7s
Build and Push / pushApp (push) Successful in 7s

This commit is contained in:
energys
2026-09-27 22:16:18 +09:00
parent 55fe4d9f43
commit 437c1ac277
+62 -5
View File
@@ -15,6 +15,8 @@ Mega SX, ZTC и др.) в формате Prometheus.
ZONT_POLL_INTERVAL — интервал опроса, сек (по умолчанию 60)
EXPORTER_PORT — порт HTTP-сервера метрик (по умолчанию 9101)
LOG_LEVEL — DEBUG/INFO/WARNING/ERROR (по умолчанию INFO)
ZONT_DOOR_INVERT — инвертировать логику двери (1=да, 0=нет, по умолчанию 0)
ZONT_DOOR_SENSOR_TYPES — через запятую, sensor_type дискретных датчиков (по умолчанию 20)
"""
import os
import time
@@ -31,7 +33,13 @@ DEVICE_ID = int(os.getenv('ZONT_DEVICE_ID'))
API_BASE = "https://my.zont.online/api"
SCRAPE_INTERVAL = int(os.getenv('SCRAPE_INTERVAL', 60))
EXPORTER_PORT = int(os.getenv('EXPORTER_PORT', 9101))
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO').upper()
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO').upper()
# Логика двери
DOOR_INVERT = os.getenv('ZONT_DOOR_INVERT', '0') == '1'
DOOR_SENSOR_TYPES = set(
int(x) for x in os.getenv('ZONT_DOOR_SENSOR_TYPES', '20').split(',') if x.strip()
)
# --- Метрики Prometheus ---
@@ -73,8 +81,28 @@ BOILER_DS = Gauge('zont_boiler_ds_celsius', 'Boiler DHW setpoint', ['device_id',
BOILER_CONNECTED = Gauge('zont_boiler_connected', 'Boiler connected (1=yes,0=no)', ['device_id', 'adapter_id'])
BOILER_FAIL = Gauge('zont_boiler_fail', 'Boiler failure (1=fail)', ['device_id', 'adapter_id'])
# Дискретные датчики (дверь, окно и т.п.) на аналоговых входах
DOOR_OPEN = Gauge(
'zont_door_open',
'Door open (1=open, 0=closed)',
['device_id', 'input_id', 'name']
)
DOOR_VOLTAGE = Gauge(
'zont_door_voltage_volts',
'Voltage on door sensor analog input',
['device_id', 'input_id', 'name']
)
# Охранные зоны (помещения)
GUARD_ZONE_ONGUARD = Gauge(
'zont_guard_zone_onguard',
'Guard zone on guard (1=yes, 0=no)',
['device_id', 'zone_id', 'name']
)
# Справочники ID -> имя для читаемых меток
_name_cache = {}
_analog_input_types = {} # {input_id: sensor_type}
auth_token = None
@@ -115,9 +143,19 @@ def build_name_cache(device):
cache[hc['id']] = hc.get('name') or f"hc_{hc['id']}"
for b in z3k.get('boiler_adapters', []):
cache[b['id']] = b.get('name') or f"boiler_{b['id']}"
for gz in z3k.get('guard_zones', []):
cache[gz['id']] = gz.get('name') or f"zone_{gz['id']}"
return cache
def build_analog_input_types(device):
"""Возвращает {input_id: sensor_type} для всех аналоговых входов."""
result = {}
for ai in device.get('z3k_config', {}).get('analog_inputs', []):
result[ai['id']] = ai.get('sensor_type')
return result
GSM_STATE_MAP = {
'not-registered': 0,
'home-network': 1,
@@ -128,7 +166,7 @@ GSM_STATE_MAP = {
def collect_metrics():
global auth_token, _name_cache
global auth_token, _name_cache, _analog_input_types
if not auth_token:
get_auth_token()
@@ -161,8 +199,9 @@ def collect_metrics():
logging.warning(f"Device {DEVICE_ID} not found in account.")
return
# Обновляем кэш имён
# Обновляем кэши
_name_cache = build_name_cache(device)
_analog_input_types = build_analog_input_types(device)
did = str(DEVICE_ID)
@@ -214,11 +253,29 @@ def collect_metrics():
if 'sensor_ok' in obj:
SENSOR_OK.labels(device_id=did, object_id=obj_id, name=name).set(1 if obj['sensor_ok'] else 0)
# Аналоговый вход (напряжение)
# Аналоговый вход для контроля напряжения (id=20550) — оставляем отдельную метрику
if isinstance(obj, dict) and 'voltage' in obj and obj_id == '20550':
if obj['voltage'] is not None:
ANALOG_VOLTAGE.labels(device_id=did, object_id=obj_id, name=name).set(obj['voltage'])
# Дискретные датчики (дверь/окно) — есть поля voltage и value,
# а в конфиге sensor_type входит в DOOR_SENSOR_TYPES
if (isinstance(obj, dict) and 'value' in obj and 'voltage' in obj
and _analog_input_types.get(int(obj_id)) in DOOR_SENSOR_TYPES):
if obj.get('voltage') is not None:
DOOR_VOLTAGE.labels(device_id=did, input_id=obj_id, name=name).set(obj['voltage'])
if obj.get('value') is not None:
door_value = obj['value']
if DOOR_INVERT:
door_value = 1 - door_value
DOOR_OPEN.labels(device_id=did, input_id=obj_id, name=name).set(door_value)
# Охранные зоны (есть поле onguard)
if isinstance(obj, dict) and 'onguard' in obj:
GUARD_ZONE_ONGUARD.labels(device_id=did, zone_id=obj_id, name=name).set(
1 if obj['onguard'] else 0
)
# Отопительные контуры
if isinstance(obj, dict) and 'target_temp' in obj and 'worktime' in obj:
if obj.get('target_temp') is not None:
@@ -257,7 +314,7 @@ if __name__ == '__main__':
exit(1)
get_auth_token()
start_http_server(EXPORTER_PORT) # <-- было 9101
start_http_server(EXPORTER_PORT)
logging.info(f"Prometheus exporter started on port {EXPORTER_PORT}")
while True: