|
45 | 45 | from homeassistant.helpers import entity_registry as er |
46 | 46 |
|
47 | 47 | from .const import ( |
| 48 | + ATTR_BACKENDS, |
48 | 49 | ATTR_CACHE_HITS, |
49 | 50 | ATTR_CACHE_HITRATE, |
50 | 51 | ATTR_CACHE_MISSES, |
@@ -223,6 +224,49 @@ def _async_sync_dynamic_rules() -> None: |
223 | 224 | _async_sync_dynamic_rules() |
224 | 225 | entry.async_on_unload(coordinator.async_add_listener(_async_sync_dynamic_rules)) |
225 | 226 |
|
| 227 | + # Backend query sensors (host entries only) |
| 228 | + if not is_group: |
| 229 | + backend_sensor_entities: dict[str, DnsdistBackendSensor] = {} |
| 230 | + |
| 231 | + @callback |
| 232 | + def _async_sync_backend_sensors() -> None: |
| 233 | + if not coordinator.data: |
| 234 | + return |
| 235 | + backends = coordinator.data.get(ATTR_BACKENDS) |
| 236 | + if not isinstance(backends, dict): |
| 237 | + backends = {} |
| 238 | + |
| 239 | + current_slugs = set(backends.keys()) |
| 240 | + known_slugs = set(backend_sensor_entities.keys()) |
| 241 | + |
| 242 | + removed_slugs = known_slugs - current_slugs |
| 243 | + ent_reg = er.async_get(hass) |
| 244 | + for slug in removed_slugs: |
| 245 | + entity = backend_sensor_entities.pop(slug, None) |
| 246 | + if entity: |
| 247 | + if entity.entity_id and ent_reg.async_get(entity.entity_id): |
| 248 | + ent_reg.async_remove(entity.entity_id) |
| 249 | + else: |
| 250 | + hass.async_create_task(entity.async_remove()) |
| 251 | + |
| 252 | + new_entities: list[DnsdistBackendSensor] = [] |
| 253 | + for slug in current_slugs: |
| 254 | + if slug in backend_sensor_entities: |
| 255 | + continue |
| 256 | + entity = DnsdistBackendSensor( |
| 257 | + coordinator=coordinator, |
| 258 | + entry_id=entry.entry_id, |
| 259 | + backend_slug=slug, |
| 260 | + ) |
| 261 | + backend_sensor_entities[slug] = entity |
| 262 | + new_entities.append(entity) |
| 263 | + |
| 264 | + if new_entities: |
| 265 | + async_add_entities(new_entities) |
| 266 | + |
| 267 | + _async_sync_backend_sensors() |
| 268 | + entry.async_on_unload(coordinator.async_add_listener(_async_sync_backend_sensors)) |
| 269 | + |
226 | 270 |
|
227 | 271 | class DnsdistSensor(CoordinatorEntity, SensorEntity): |
228 | 272 | """Representation of a dnsdist metric sensor (host or group).""" |
@@ -477,3 +521,56 @@ def extra_state_attributes(self) -> dict[str, Any]: |
477 | 521 | @property |
478 | 522 | def device_info(self) -> DeviceInfo: |
479 | 523 | return build_device_info(self.coordinator, self._is_group) |
| 524 | + |
| 525 | + |
| 526 | +class DnsdistBackendSensor(CoordinatorEntity, SensorEntity): |
| 527 | + """Sensor tracking query count for a dnsdist backend server.""" |
| 528 | + |
| 529 | + _attr_has_entity_name = False |
| 530 | + _attr_should_poll = False |
| 531 | + _attr_state_class = SensorStateClass.TOTAL_INCREASING |
| 532 | + _attr_icon = "mdi:dns" |
| 533 | + |
| 534 | + def __init__(self, *, coordinator, entry_id: str, backend_slug: str) -> None: |
| 535 | + super().__init__(coordinator) |
| 536 | + self._slug = backend_slug |
| 537 | + self._attr_unique_id = f"{entry_id}:backend_queries:{backend_slug}" |
| 538 | + self._attr_native_unit_of_measurement = COUNT |
| 539 | + |
| 540 | + def _backend_data(self) -> dict[str, Any]: |
| 541 | + data = self.coordinator.data or {} |
| 542 | + backends = data.get(ATTR_BACKENDS, {}) if isinstance(data, dict) else {} |
| 543 | + if isinstance(backends, dict): |
| 544 | + return backends.get(self._slug, {}) |
| 545 | + return {} |
| 546 | + |
| 547 | + @property |
| 548 | + def name(self) -> str: |
| 549 | + host = getattr(self.coordinator, "_name", "dnsdist") |
| 550 | + backend = self._backend_data() |
| 551 | + address = backend.get("address") or self._slug |
| 552 | + name = backend.get("name") |
| 553 | + if name: |
| 554 | + return f"{host} Backend {name} Queries" |
| 555 | + return f"{host} Backend {address} Queries" |
| 556 | + |
| 557 | + @property |
| 558 | + def native_value(self): |
| 559 | + backend = self._backend_data() |
| 560 | + queries = backend.get("queries") |
| 561 | + if isinstance(queries, (int, float)): |
| 562 | + return int(queries) |
| 563 | + return None |
| 564 | + |
| 565 | + @property |
| 566 | + def extra_state_attributes(self) -> dict[str, Any]: |
| 567 | + backend = self._backend_data() |
| 568 | + attrs: dict[str, Any] = {} |
| 569 | + for key in ("address", "name", "responses", "drops", "latency", "qps", "outstanding"): |
| 570 | + if key in backend and backend[key] is not None: |
| 571 | + attrs[key] = backend[key] |
| 572 | + return attrs |
| 573 | + |
| 574 | + @property |
| 575 | + def device_info(self) -> DeviceInfo: |
| 576 | + return build_device_info(self.coordinator, False) |
0 commit comments