diff --git a/zont-exporter/zont_exporter.py b/zont-exporter/zont_exporter.py index f9c9bd7..7df101f 100644 --- a/zont-exporter/zont_exporter.py +++ b/zont-exporter/zont_exporter.py @@ -108,12 +108,18 @@ HEATING_TARGET_TEMP = Gauge('zont_heating_target_temp_celsius', 'Heating circuit HEATING_SETPOINT_TEMP = Gauge('zont_heating_setpoint_temp_celsius', 'Heating circuit setpoint temperature', ['device_id', 'circuit_id', 'name']) HEATING_WORKTIME = Gauge('zont_heating_worktime_seconds', 'Heating circuit work time in last minute', ['device_id', 'circuit_id', 'name']) HEATING_STATUS = Gauge('zont_heating_status', 'Heating circuit status code', ['device_id', 'circuit_id', 'name']) +# Фактическая температура контура отопления (подача теплоносителя) +HEATING_FLOW_TEMP = Gauge('zont_heating_flow_temp_celsius', 'Heating circuit flow temperature (actual)', ['device_id', 'circuit_id', 'name']) # Котёл / OpenTherm BOILER_CS = Gauge('zont_boiler_cs_celsius', 'Boiler calculated CH water temp', ['device_id', 'adapter_id']) BOILER_DS = Gauge('zont_boiler_ds_celsius', 'Boiler DHW setpoint', ['device_id', 'adapter_id']) +BOILER_BT = Gauge('zont_boiler_bt_celsius', 'Boiler actual flow water temp (OpenTherm)', ['device_id', 'adapter_id']) +BOILER_DT = Gauge('zont_boiler_dt_celsius', 'Boiler actual DHW temp (OpenTherm)', ['device_id', 'adapter_id']) +BOILER_OT = Gauge('zont_boiler_outdoor_celsius', 'Boiler outdoor temperature (OpenTherm)', ['device_id', 'adapter_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']) +BOILER_FLAME = Gauge('zont_boiler_flame', 'Boiler flame on (1=on,0=off)', ['device_id', 'adapter_id']) # Дискретные датчики DOOR_OPEN = Gauge('zont_door_open', 'Door open (1=open, 0=closed)', ['device_id', 'input_id', 'name']) @@ -498,7 +504,7 @@ def collect_events(did): _last_event_poll_time = now logging.debug(f"raw_events returned {len(events)} events for window [{mintime}, {now}]") - + if not events: return @@ -624,6 +630,52 @@ def collect_metrics(): if 'internet_weather' in device: INTERNET_WEATHER.labels(device_id=did).set(device['internet_weather']) + # --- last-boiler-state (для ZONT H-1/H-2/H-1V с OpenTherm) --- + last_boiler = io.get('last-boiler-state', {}) + if last_boiler: + ot = last_boiler.get('ot', {}) or {} + + # Фактическая температура теплоносителя (подача) — то, что нужно + if ot.get('bt') is not None: + HEATING_FLOW_TEMP.labels( + device_id=did, circuit_id='boiler', name='boiler_flow' + ).set(ot['bt']) + BOILER_BT.labels(device_id=did, adapter_id='boiler').set(ot['bt']) + + # Расчётная температура теплоносителя + if ot.get('cs') is not None: + BOILER_CS.labels(device_id=did, adapter_id='boiler').set(ot['cs']) + + # Фактическая температура ГВС + if ot.get('dt') is not None: + BOILER_DT.labels(device_id=did, adapter_id='boiler').set(ot['dt']) + + # Уличная температура по данным котла + if ot.get('ot') is not None: + BOILER_OT.labels(device_id=did, adapter_id='boiler').set(ot['ot']) + + # Флаг работы горелки + flags = ot.get('s', []) or [] + BOILER_FLAME.labels(device_id=did, adapter_id='boiler').set( + 1 if 'fl' in flags else 0 + ) + + # Авария котла + ff = ot.get('ff', {}) or {} + BOILER_FAIL.labels(device_id=did, adapter_id='boiler').set( + 1 if (ff.get('c') or ff.get('f')) else 0 + ) + + # Целевая температура и время работы + if last_boiler.get('target_temp') is not None: + HEATING_TARGET_TEMP.labels( + device_id=did, circuit_id='boiler', name='boiler_target' + ).set(last_boiler['target_temp']) + if last_boiler.get('boiler_work_time') is not None: + HEATING_WORKTIME.labels( + device_id=did, circuit_id='boiler', name='boiler_worktime' + ).set(last_boiler['boiler_work_time']) + # --- z3k-state --- z3k_state = io.get('z3k-state', {}) for obj_id, obj in z3k_state.items(): @@ -669,6 +721,11 @@ def collect_metrics(): if obj.get('status') is not None: HEATING_STATUS.labels(device_id=did, circuit_id=obj_id, name=name).set(obj['status']) + # Фактическая температура контура (подача) — возможные имена полей + flow_temp = obj.get('flow_temp') or obj.get('curr_temp') or obj.get('bt') + if flow_temp is not None: + HEATING_FLOW_TEMP.labels(device_id=did, circuit_id=obj_id, name=name).set(flow_temp) + # Адаптер котла / OpenTherm if isinstance(obj, dict) and 'ot' in obj and 'status' in obj: ot = obj.get('ot', {}) or {} @@ -676,6 +733,17 @@ def collect_metrics(): BOILER_CS.labels(device_id=did, adapter_id=obj_id).set(ot['cs']) if ot.get('ds') is not None: BOILER_DS.labels(device_id=did, adapter_id=obj_id).set(ot['ds']) + if ot.get('bt') is not None: + BOILER_BT.labels(device_id=did, adapter_id=obj_id).set(ot['bt']) + HEATING_FLOW_TEMP.labels(device_id=did, circuit_id=obj_id, name=name).set(ot['bt']) + if ot.get('dt') is not None: + BOILER_DT.labels(device_id=did, adapter_id=obj_id).set(ot['dt']) + if ot.get('ot') is not None: + BOILER_OT.labels(device_id=did, adapter_id=obj_id).set(ot['ot']) + + flags = ot.get('s', []) or [] + BOILER_FLAME.labels(device_id=did, adapter_id=obj_id).set(1 if 'fl' in flags else 0) + status = obj.get('status', {}) BOILER_CONNECTED.labels(device_id=did, adapter_id=obj_id).set( 0 if status.get('no_boiler_connection') else 1