diff --git a/services/mqtt_publisher.py b/services/mqtt_publisher.py index 23c1bd0..c3c9b59 100644 --- a/services/mqtt_publisher.py +++ b/services/mqtt_publisher.py @@ -80,7 +80,10 @@ class MqttPublisher: self.discovery_prefix = os.environ.get("MQTT_DISCOVERY_PREFIX", "homeassistant") self.base_topic = os.environ.get("MQTT_BASE_TOPIC", "jbod-monitor") self.interval = int(os.environ.get("MQTT_PUBLISH_INTERVAL", "60")) - self.node = _slug(get_node_id()) + self.node_raw = get_node_id() + self.node = _slug(self.node_raw) + # Parent "hub" device that the per-enclosure devices nest under. + self.hub_id = f"{self.node}_jbod_monitor" self.availability_topic = f"{self.base_topic}/{self.node}/status" # unique_ids for which we've already published discovery this connection. @@ -127,6 +130,16 @@ class MqttPublisher: logger.warning("MQTT disconnected: %s", reason_code) # discovery / state ------------------------------------------------------ + def _hub_device_block(self) -> dict: + """The parent device the enclosures nest under (named, so HA doesn't + show an 'Unnamed Device').""" + return { + "identifiers": [self.hub_id], + "name": f"JBOD Monitor ({self.node_raw})", + "manufacturer": "jbod-monitor", + "model": "SES/SMART poller", + } + def _device_block(self, enc: dict) -> dict: enc_id = enc["id"] vendor = enc.get("vendor", "").strip() @@ -137,9 +150,29 @@ class MqttPublisher: "name": f"JBOD {name} ({enc_id})", "manufacturer": vendor or None, "model": model or None, - "via_device": f"{self.node}_jbod_monitor", + "via_device": self.hub_id, } + def _publish_hub(self) -> None: + """Announce the hub device via a connectivity binary_sensor, so the + parent device is named and shows monitor online/offline status.""" + uid = f"{self.hub_id}_status" + if uid in self._discovered: + return + payload = { + "name": "Status", + "unique_id": uid, + "device_class": "connectivity", + "state_topic": self.availability_topic, + "payload_on": "online", + "payload_off": "offline", + "entity_category": "diagnostic", + "device": self._hub_device_block(), + } + topic = f"{self.discovery_prefix}/binary_sensor/{self.hub_id}/status/config" + self._client.publish(topic, json.dumps(payload), qos=1, retain=True) + self._discovered.add(uid) + def _publish_discovery(self, enc: dict) -> None: enc_id = enc["id"] enc_slug = _slug(enc_id) @@ -204,6 +237,7 @@ class MqttPublisher: self._client.publish(state_topic, json.dumps(payload), qos=0, retain=True) def publish_snapshot(self, snapshot: dict) -> None: + self._publish_hub() # name the parent device before nesting enclosures for enc in snapshot.get("enclosures", []): self._publish_discovery(enc) self._publish_state(enc)