|
31 | 31 | device_registry as dr, |
32 | 32 | entity_registry as er, |
33 | 33 | ) |
| 34 | +from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED |
34 | 35 | from homeassistant.helpers.dispatcher import ( |
35 | 36 | async_dispatcher_connect, |
36 | 37 | async_dispatcher_send, |
|
44 | 45 | ) |
45 | 46 | from homeassistant.helpers.entity_platform import AddEntitiesCallback |
46 | 47 | from homeassistant.helpers.reload import async_setup_reload_service |
47 | | -from homeassistant.helpers.typing import ConfigType |
| 48 | +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType |
48 | 49 |
|
49 | 50 | from . import ( |
50 | 51 | DATA_MQTT, |
|
54 | 55 | debug_info, |
55 | 56 | subscription, |
56 | 57 | ) |
| 58 | +from ..mqtt import publish |
57 | 59 | from .const import ( |
58 | 60 | ATTR_DISCOVERY_HASH, |
59 | 61 | ATTR_DISCOVERY_PAYLOAD, |
@@ -520,8 +522,112 @@ async def cleanup_device_registry( |
520 | 522 | ) |
521 | 523 |
|
522 | 524 |
|
| 525 | +class MqttDiscoveryDeviceUpdateService: |
| 526 | + """Add support for auto discovery for platforms without an entity.""" |
| 527 | + |
| 528 | + def __init__( |
| 529 | + self, |
| 530 | + hass: HomeAssistant, |
| 531 | + log_name: str, |
| 532 | + discovery_data: dict[str, Any] | None, |
| 533 | + device_id: str | None, |
| 534 | + config_entry: ConfigEntry, |
| 535 | + ) -> None: |
| 536 | + """Initialize the update service.""" |
| 537 | + |
| 538 | + # Only activate update service id the parent class has a discover hash |
| 539 | + if discovery_data is None: |
| 540 | + return |
| 541 | + |
| 542 | + self.hass = hass |
| 543 | + config_entry_id = config_entry.entry_id |
| 544 | + discovery_hash = discovery_data[ATTR_DISCOVERY_HASH] |
| 545 | + discovery_topic = discovery_data[ATTR_DISCOVERY_TOPIC] |
| 546 | + _device_removed: bool = False |
| 547 | + |
| 548 | + async def async_discovery_update( |
| 549 | + discovery_payload: DiscoveryInfoType | None, |
| 550 | + ) -> None: |
| 551 | + """Handle discovery update.""" |
| 552 | + if not discovery_payload: |
| 553 | + # unregister the service through auto discovery |
| 554 | + async_dispatcher_send( |
| 555 | + hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None |
| 556 | + ) |
| 557 | + await _async_tear_down() |
| 558 | + return |
| 559 | + |
| 560 | + # update the service through auto discovery |
| 561 | + await self.async_discovery_update(discovery_payload) |
| 562 | + _LOGGER.debug( |
| 563 | + "%s %s update has been processed", |
| 564 | + log_name, |
| 565 | + discovery_hash, |
| 566 | + ) |
| 567 | + async_dispatcher_send( |
| 568 | + hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None |
| 569 | + ) |
| 570 | + |
| 571 | + async def async_device_removed(event): |
| 572 | + """Handle the removal of a device.""" |
| 573 | + nonlocal _device_removed |
| 574 | + nonlocal _device_removed |
| 575 | + if _device_removed or not async_removed_from_device( |
| 576 | + hass, event, device_id, config_entry_id |
| 577 | + ): |
| 578 | + return |
| 579 | + _device_removed = True |
| 580 | + # Clear the discovery topic so the service is not rediscovered after a restart |
| 581 | + publish(hass, discovery_topic, "", retain=True) |
| 582 | + await _async_tear_down() |
| 583 | + |
| 584 | + async def _async_tear_down() -> None: |
| 585 | + """Handle the removal of the service.""" |
| 586 | + nonlocal _device_removed, self |
| 587 | + await self.async_tear_down() |
| 588 | + # remove the service for auto discovery updates and clean up the device registry |
| 589 | + if not _device_removed and config_entry: |
| 590 | + _device_removed = True |
| 591 | + await cleanup_device_registry(hass, device_id, config_entry_id) |
| 592 | + clear_discovery_hash(hass, discovery_hash) |
| 593 | + _remove_discovery() |
| 594 | + _LOGGER.info( |
| 595 | + "%s %s has been removed", |
| 596 | + log_name, |
| 597 | + discovery_hash, |
| 598 | + ) |
| 599 | + del self |
| 600 | + |
| 601 | + _remove_discovery = async_dispatcher_connect( |
| 602 | + hass, |
| 603 | + MQTT_DISCOVERY_UPDATED.format(discovery_hash), |
| 604 | + async_discovery_update, |
| 605 | + ) |
| 606 | + if device_id is not None: |
| 607 | + self._remove_device_updated = hass.bus.async_listen( |
| 608 | + EVENT_DEVICE_REGISTRY_UPDATED, async_device_removed |
| 609 | + ) |
| 610 | + async_dispatcher_send(hass, MQTT_DISCOVERY_DONE.format(discovery_hash), None) |
| 611 | + _LOGGER.info( |
| 612 | + "%s %s has been initialized", |
| 613 | + log_name, |
| 614 | + discovery_hash, |
| 615 | + ) |
| 616 | + |
| 617 | + async def async_tear_down(self) -> None: |
| 618 | + """Handle the cleanup of platform specific parts.""" |
| 619 | + raise NotImplementedError() |
| 620 | + |
| 621 | + async def async_discovery_update( |
| 622 | + self, |
| 623 | + discovery_payload: DiscoveryInfoType, |
| 624 | + ) -> None: |
| 625 | + """Update the configuration through discovery.""" |
| 626 | + raise NotImplementedError() |
| 627 | + |
| 628 | + |
523 | 629 | class MqttDiscoveryUpdate(Entity): |
524 | | - """Mixin used to handle updated discovery message.""" |
| 630 | + """Mixin used to handle updated discovery message for entity based platforms.""" |
525 | 631 |
|
526 | 632 | def __init__(self, discovery_data, discovery_update=None) -> None: |
527 | 633 | """Initialize the discovery update mixin.""" |
|
0 commit comments