diff --git a/homeassistant/components/github/__init__.py b/homeassistant/components/github/__init__.py index 58da7c9c5fef44..193ec28ab99851 100644 --- a/homeassistant/components/github/__init__.py +++ b/homeassistant/components/github/__init__.py @@ -25,8 +25,11 @@ from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.core import Config, HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, + UpdateFailed, +) from .const import ( CONF_CLONES, @@ -193,14 +196,14 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): return unload_ok -class GitHubEntity(Entity): +class GitHubEntity(CoordinatorEntity): """Defines a GitHub entity.""" def __init__( self, coordinator: DataUpdateCoordinator, unique_id: str, name: str, icon: str ) -> None: """Set up GitHub Entity.""" - self._coordinator = coordinator + super().__init__(coordinator) self._unique_id = unique_id self._name = name self._icon = icon @@ -224,29 +227,7 @@ def icon(self) -> str: @property def available(self) -> bool: """Return True if entity is available.""" - return self._coordinator.last_update_success and self._available - - @property - def should_poll(self): - """No need to poll. Coordinator notifies entity of updates.""" - return False - - async def async_added_to_hass(self): - """When entity is added to hass.""" - self.async_on_remove( - self._coordinator.async_add_listener(self.async_write_ha_state) - ) - - async def async_update(self) -> None: - """Update GitHub entity.""" - if await self._github_update(): - self._available = True - else: - self._available = False - - async def _github_update(self) -> bool: - """Update GitHub entity.""" - raise NotImplementedError() + return self.coordinator.last_update_success and self._available class GitHubDeviceEntity(GitHubEntity): @@ -255,7 +236,7 @@ class GitHubDeviceEntity(GitHubEntity): @property def device_info(self) -> Dict[str, Any]: """Return device information about this GitHub instance.""" - data: GitHubData = self._coordinator.data + data: GitHubData = self.coordinator.data return { "entry_type": "service", diff --git a/homeassistant/components/github/sensor.py b/homeassistant/components/github/sensor.py index a4de7dbd3c5924..55e631b03750a1 100644 --- a/homeassistant/components/github/sensor.py +++ b/homeassistant/components/github/sensor.py @@ -119,16 +119,6 @@ def __init__( super().__init__(coordinator, f"{repository.full_name}_{unique_id}", name, icon) - @property - def state(self) -> str: - """Return the state of the sensor.""" - return self._state - - @property - def device_state_attributes(self) -> object: - """Return the attributes of the sensor.""" - return self._attributes - class ClonesSensor(GitHubSensor): """Representation of a Repository Sensor.""" @@ -142,14 +132,17 @@ def __init__( coordinator, repository, "clones", f"{name} Clones", "mdi:github" ) - async def _github_update(self) -> bool: - """Fetch new state data for the sensor.""" - await self._coordinator.async_request_refresh() - data: GitHubData = self._coordinator.data - - self._state = data.clones.count + @property + def state(self) -> str: + """Return the state of the sensor.""" + data: GitHubData = self.coordinator.data + return data.clones.count - self._attributes = { + @property + def device_state_attributes(self) -> object: + """Return the attributes of the sensor.""" + data: GitHubData = self.coordinator.data + return { ATTR_REPO_DESCRIPTION: data.repository.description, ATTR_REPO_HOMEPAGE: data.repository.homepage, ATTR_REPO_NAME: data.repository.name, @@ -158,8 +151,6 @@ async def _github_update(self) -> bool: ATTR_UNIQUE: data.clones.uniques, } - return True - class ForksSensor(GitHubSensor): """Representation of a Repository Sensor.""" @@ -173,14 +164,17 @@ def __init__( coordinator, repository, "forks", f"{name} Forks", "mdi:github" ) - async def _github_update(self) -> bool: - """Fetch new state data for the sensor.""" - await self._coordinator.async_request_refresh() - data: GitHubData = self._coordinator.data - - self._state = data.repository.forks_count + @property + def state(self) -> str: + """Return the state of the sensor.""" + data: GitHubData = self.coordinator.data + return data.repository.forks_count - self._attributes = { + @property + def device_state_attributes(self) -> object: + """Return the attributes of the sensor.""" + data: GitHubData = self.coordinator.data + return { ATTR_REPO_DESCRIPTION: data.repository.description, ATTR_REPO_HOMEPAGE: data.repository.homepage, ATTR_REPO_NAME: data.repository.name, @@ -188,8 +182,6 @@ async def _github_update(self) -> bool: ATTR_REPO_TOPICS: data.repository.topics, } - return True - class LatestCommitSensor(GitHubSensor): """Representation of a Repository Sensor.""" @@ -207,14 +199,17 @@ def __init__( "mdi:github", ) - async def _github_update(self) -> bool: - """Fetch new state data for the sensor.""" - await self._coordinator.async_request_refresh() - data: GitHubData = self._coordinator.data - - self._state = short_sha(data.latest_commit.sha) + @property + def state(self) -> str: + """Return the state of the sensor.""" + data: GitHubData = self.coordinator.data + return short_sha(data.latest_commit.sha) - self._attributes = { + @property + def device_state_attributes(self) -> object: + """Return the attributes of the sensor.""" + data: GitHubData = self.coordinator.data + return { ATTR_MESSAGE: short_message(data.latest_commit.commit.message), ATTR_REPO_DESCRIPTION: data.repository.description, ATTR_REPO_HOMEPAGE: data.repository.homepage, @@ -226,8 +221,6 @@ async def _github_update(self) -> bool: ATTR_USER: data.latest_commit.author.login, } - return True - class LatestOpenIssueSensor(GitHubSensor): """Representation of a Repository Sensor.""" @@ -245,21 +238,24 @@ def __init__( "mdi:github", ) - async def _github_update(self) -> bool: - """Fetch new state data for the sensor.""" - await self._coordinator.async_request_refresh() - data: GitHubData = self._coordinator.data - + @property + def state(self) -> str: + """Return the state of the sensor.""" + data: GitHubData = self.coordinator.data if data.open_issues is None or len(data.open_issues) < 1: - return False - - self._state = data.open_issues[0].title + return None + return data.open_issues[0].title + @property + def device_state_attributes(self) -> object: + """Return the attributes of the sensor.""" + data: GitHubData = self.coordinator.data + if data.open_issues is None or len(data.open_issues) < 1: + return None labels = [] for label in data.open_issues[0].labels: labels.append(label.get("name")) - - self._attributes = { + return { ATTR_ASSIGNEES: data.open_issues[0].assignees, ATTR_ID: data.open_issues[0].id, ATTR_LABELS: labels, @@ -274,8 +270,6 @@ async def _github_update(self) -> bool: ATTR_USER: data.open_issues[0].user.login, } - return True - class LatestPullRequestSensor(GitHubSensor): """Representation of a Repository Sensor.""" @@ -293,21 +287,24 @@ def __init__( "mdi:github", ) - async def _github_update(self) -> bool: - """Fetch new state data for the sensor.""" - await self._coordinator.async_request_refresh() - data: GitHubData = self._coordinator.data - + @property + def state(self) -> str: + """Return the state of the sensor.""" + data: GitHubData = self.coordinator.data if data.open_pull_requests is None or len(data.open_pull_requests) < 1: - return False - - self._state = data.open_pull_requests[0].title + return None + return data.open_pull_requests[0].title + @property + def device_state_attributes(self) -> object: + """Return the attributes of the sensor.""" + data: GitHubData = self.coordinator.data + if data.open_pull_requests is None or len(data.open_pull_requests) < 1: + return None labels = [] for label in data.open_pull_requests[0].labels: labels.append(label.get("name")) - - self._attributes = { + return { ATTR_ASSIGNEES: data.open_pull_requests[0].assignees, ATTR_ID: data.open_pull_requests[0].id, ATTR_LABELS: labels, @@ -321,8 +318,6 @@ async def _github_update(self) -> bool: ATTR_USER: data.open_pull_requests[0].user.login, } - return True - class LatestReleaseSensor(GitHubSensor): """Representation of a Repository Sensor.""" @@ -340,17 +335,21 @@ def __init__( "mdi:github", ) - async def _github_update(self) -> bool: - """Fetch new state data for the sensor.""" - await self._coordinator.async_request_refresh() - data: GitHubData = self._coordinator.data - + @property + def state(self) -> str: + """Return the state of the sensor.""" + data: GitHubData = self.coordinator.data if data.releases is None or len(data.releases) < 1: - return False + return None + return data.releases[0].tag_name - self._state = data.releases[0].tag_name - - self._attributes = { + @property + def device_state_attributes(self) -> object: + """Return the attributes of the sensor.""" + data: GitHubData = self.coordinator.data + if data.releases is None or len(data.releases) < 1: + return None + return { ATTR_DATE: data.releases[0].published_at, ATTR_DRAFT: data.releases[0].draft, ATTR_NAME: data.releases[0].name, @@ -364,8 +363,6 @@ async def _github_update(self) -> bool: ATTR_URL: f"https://github.com/{data.repository.full_name}/releases/{data.releases[0].tag_name}", } - return True - class StargazersSensor(GitHubSensor): """Representation of a Repository Sensor.""" @@ -379,14 +376,17 @@ def __init__( coordinator, repository, "stargazers", f"{name} Stargazers", "mdi:github" ) - async def _github_update(self) -> bool: - """Fetch new state data for the sensor.""" - await self._coordinator.async_request_refresh() - data: GitHubData = self._coordinator.data - - self._state = data.repository.stargazers_count + @property + def state(self) -> str: + """Return the state of the sensor.""" + data: GitHubData = self.coordinator.data + return data.repository.stargazers_count - self._attributes = { + @property + def device_state_attributes(self) -> object: + """Return the attributes of the sensor.""" + data: GitHubData = self.coordinator.data + return { ATTR_REPO_DESCRIPTION: data.repository.description, ATTR_REPO_HOMEPAGE: data.repository.homepage, ATTR_REPO_NAME: data.repository.name, @@ -394,8 +394,6 @@ async def _github_update(self) -> bool: ATTR_REPO_TOPICS: data.repository.topics, } - return True - class ViewsSensor(GitHubSensor): """Representation of a Repository Sensor.""" @@ -409,14 +407,17 @@ def __init__( coordinator, repository, "views", f"{name} Views", "mdi:github" ) - async def _github_update(self) -> bool: - """Fetch new state data for the sensor.""" - await self._coordinator.async_request_refresh() - data: GitHubData = self._coordinator.data - - self._state = data.views.count + @property + def state(self) -> str: + """Return the state of the sensor.""" + data: GitHubData = self.coordinator.data + return data.views.count - self._attributes = { + @property + def device_state_attributes(self) -> object: + """Return the attributes of the sensor.""" + data: GitHubData = self.coordinator.data + return { ATTR_REPO_DESCRIPTION: data.repository.description, ATTR_REPO_HOMEPAGE: data.repository.homepage, ATTR_REPO_NAME: data.repository.name, @@ -425,8 +426,6 @@ async def _github_update(self) -> bool: ATTR_UNIQUE: data.views.uniques, } - return True - class WatchersSensor(GitHubSensor): """Representation of a Repository Sensor.""" @@ -440,19 +439,20 @@ def __init__( coordinator, repository, "watchers", f"{name} Watchers", "mdi:github" ) - async def _github_update(self) -> bool: - """Fetch new state data for the sensor.""" - await self._coordinator.async_request_refresh() - data: GitHubData = self._coordinator.data - - self._state = data.repository.watchers_count + @property + def state(self) -> str: + """Return the state of the sensor.""" + data: GitHubData = self.coordinator.data + return data.repository.watchers_count - self._attributes = { + @property + def device_state_attributes(self) -> object: + """Return the attributes of the sensor.""" + data: GitHubData = self.coordinator.data + return { ATTR_REPO_DESCRIPTION: data.repository.description, ATTR_REPO_HOMEPAGE: data.repository.homepage, ATTR_REPO_NAME: data.repository.name, ATTR_REPO_PATH: data.repository.full_name, ATTR_REPO_TOPICS: data.repository.topics, } - - return True