update.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. """Update entities for HACS."""
  2. from __future__ import annotations
  3. from typing import Any
  4. from homeassistant.components.update import UpdateEntity
  5. from homeassistant.core import callback
  6. from homeassistant.helpers.dispatcher import async_dispatcher_connect
  7. from .base import HacsBase
  8. from .const import DOMAIN
  9. from .entity import HacsRepositoryEntity
  10. from .enums import HacsCategory, HacsDispatchEvent
  11. async def async_setup_entry(hass, _config_entry, async_add_devices):
  12. """Setup update platform."""
  13. hacs: HacsBase = hass.data.get(DOMAIN)
  14. async_add_devices(
  15. HacsRepositoryUpdateEntity(hacs=hacs, repository=repository)
  16. for repository in hacs.repositories.list_downloaded
  17. )
  18. class HacsRepositoryUpdateEntity(HacsRepositoryEntity, UpdateEntity):
  19. """Update entities for repositories downloaded with HACS."""
  20. @property
  21. def supported_features(self) -> int | None:
  22. """Return the supported features of the entity."""
  23. features = 4 | 16
  24. if self.repository.can_download:
  25. features = features | 1
  26. return features
  27. @property
  28. def name(self) -> str | None:
  29. """Return the name."""
  30. return f"{self.repository.display_name} update"
  31. @property
  32. def latest_version(self) -> str:
  33. """Return latest version of the entity."""
  34. return self.repository.display_available_version
  35. @property
  36. def release_url(self) -> str:
  37. """Return the URL of the release page."""
  38. if self.repository.display_version_or_commit == "commit":
  39. return f"https://github.com/{self.repository.data.full_name}"
  40. return f"https://github.com/{self.repository.data.full_name}/releases/{self.latest_version}"
  41. @property
  42. def installed_version(self) -> str:
  43. """Return downloaded version of the entity."""
  44. return self.repository.display_installed_version
  45. @property
  46. def release_summary(self) -> str | None:
  47. """Return the release summary."""
  48. if not self.repository.can_download:
  49. return f"<ha-alert alert-type='warning'>Requires Home Assistant {self.repository.repository_manifest.homeassistant}</ha-alert>"
  50. if self.repository.pending_restart:
  51. return "<ha-alert alert-type='error'>Restart of Home Assistant required</ha-alert>"
  52. return None
  53. @property
  54. def entity_picture(self) -> str | None:
  55. """Return the entity picture to use in the frontend."""
  56. if (
  57. self.repository.data.category != HacsCategory.INTEGRATION
  58. or self.repository.data.domain is None
  59. ):
  60. return None
  61. return f"https://brands.home-assistant.io/_/{self.repository.data.domain}/icon.png"
  62. async def async_install(self, version: str | None, backup: bool, **kwargs: Any) -> None:
  63. """Install an update."""
  64. if self.repository.display_version_or_commit == "version":
  65. self._update_in_progress(progress=10)
  66. self.repository.data.selected_tag = self.latest_version
  67. await self.repository.update_repository(force=True)
  68. self._update_in_progress(progress=20)
  69. await self.repository.async_install()
  70. self._update_in_progress(progress=False)
  71. async def async_release_notes(self) -> str | None:
  72. """Return the release notes."""
  73. if self.repository.pending_restart or not self.repository.can_download:
  74. return None
  75. release_notes = ""
  76. if len(self.repository.releases.objects) > 0:
  77. release = self.repository.releases.objects[0]
  78. release_notes += release.body
  79. if self.repository.pending_update:
  80. if self.repository.data.category == HacsCategory.INTEGRATION:
  81. release_notes += (
  82. "\n\n<ha-alert alert-type='warning'>You need to restart"
  83. " Home Assistant manually after updating.</ha-alert>\n\n"
  84. )
  85. if self.repository.data.category == HacsCategory.PLUGIN:
  86. release_notes += (
  87. "\n\n<ha-alert alert-type='warning'>You need to manually"
  88. " clear the frontend cache after updating.</ha-alert>\n\n"
  89. )
  90. return release_notes.replace("\n#", "\n\n#")
  91. async def async_added_to_hass(self) -> None:
  92. """Register for status events."""
  93. await super().async_added_to_hass()
  94. self.async_on_remove(
  95. async_dispatcher_connect(
  96. self.hass,
  97. HacsDispatchEvent.REPOSITORY_DOWNLOAD_PROGRESS,
  98. self._update_download_progress,
  99. )
  100. )
  101. @callback
  102. def _update_download_progress(self, data: dict) -> None:
  103. """Update the download progress."""
  104. if data["repository"] != self.repository.data.full_name:
  105. return
  106. self._update_in_progress(progress=data["progress"])
  107. @callback
  108. def _update_in_progress(self, progress: int | bool) -> None:
  109. """Update the download progress."""
  110. self._attr_in_progress = progress
  111. self.async_write_ha_state()