Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Sensor platform for the GitHub integration."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable, Mapping
6 from dataclasses import dataclass
7 from typing import Any
8 
10  SensorEntity,
11  SensorEntityDescription,
12  SensorStateClass,
13 )
14 from homeassistant.const import EntityCategory
15 from homeassistant.core import HomeAssistant
16 from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
17 from homeassistant.helpers.entity_platform import AddEntitiesCallback
18 from homeassistant.helpers.typing import StateType
19 from homeassistant.helpers.update_coordinator import CoordinatorEntity
20 
21 from . import GithubConfigEntry
22 from .const import DOMAIN
23 from .coordinator import GitHubDataUpdateCoordinator
24 
25 
26 @dataclass(frozen=True, kw_only=True)
28  """Describes GitHub issue sensor entity."""
29 
30  value_fn: Callable[[dict[str, Any]], StateType]
31 
32  attr_fn: Callable[[dict[str, Any]], Mapping[str, Any] | None] = lambda data: None
33  avabl_fn: Callable[[dict[str, Any]], bool] = lambda data: True
34 
35 
36 SENSOR_DESCRIPTIONS: tuple[GitHubSensorEntityDescription, ...] = (
38  key="discussions_count",
39  translation_key="discussions_count",
40  entity_category=EntityCategory.DIAGNOSTIC,
41  state_class=SensorStateClass.MEASUREMENT,
42  value_fn=lambda data: data["discussion"]["total"],
43  ),
45  key="stargazers_count",
46  translation_key="stargazers_count",
47  entity_category=EntityCategory.DIAGNOSTIC,
48  state_class=SensorStateClass.MEASUREMENT,
49  value_fn=lambda data: data["stargazers_count"],
50  ),
52  key="subscribers_count",
53  translation_key="subscribers_count",
54  entity_category=EntityCategory.DIAGNOSTIC,
55  state_class=SensorStateClass.MEASUREMENT,
56  value_fn=lambda data: data["watchers"]["total"],
57  ),
59  key="forks_count",
60  translation_key="forks_count",
61  entity_category=EntityCategory.DIAGNOSTIC,
62  state_class=SensorStateClass.MEASUREMENT,
63  value_fn=lambda data: data["forks_count"],
64  ),
66  key="issues_count",
67  translation_key="issues_count",
68  entity_category=EntityCategory.DIAGNOSTIC,
69  state_class=SensorStateClass.MEASUREMENT,
70  value_fn=lambda data: data["issue"]["total"],
71  ),
73  key="pulls_count",
74  translation_key="pulls_count",
75  entity_category=EntityCategory.DIAGNOSTIC,
76  state_class=SensorStateClass.MEASUREMENT,
77  value_fn=lambda data: data["pull_request"]["total"],
78  ),
80  key="latest_commit",
81  translation_key="latest_commit",
82  value_fn=lambda data: data["default_branch_ref"]["commit"]["message"][:255],
83  attr_fn=lambda data: {
84  "sha": data["default_branch_ref"]["commit"]["sha"],
85  "url": data["default_branch_ref"]["commit"]["url"],
86  },
87  ),
89  key="latest_discussion",
90  translation_key="latest_discussion",
91  avabl_fn=lambda data: data["discussion"]["discussions"],
92  value_fn=lambda data: data["discussion"]["discussions"][0]["title"][:255],
93  attr_fn=lambda data: {
94  "url": data["discussion"]["discussions"][0]["url"],
95  "number": data["discussion"]["discussions"][0]["number"],
96  },
97  ),
99  key="latest_release",
100  translation_key="latest_release",
101  avabl_fn=lambda data: data["release"] is not None,
102  value_fn=lambda data: data["release"]["name"][:255],
103  attr_fn=lambda data: {
104  "url": data["release"]["url"],
105  "tag": data["release"]["tag"],
106  },
107  ),
109  key="latest_issue",
110  translation_key="latest_issue",
111  avabl_fn=lambda data: data["issue"]["issues"],
112  value_fn=lambda data: data["issue"]["issues"][0]["title"][:255],
113  attr_fn=lambda data: {
114  "url": data["issue"]["issues"][0]["url"],
115  "number": data["issue"]["issues"][0]["number"],
116  },
117  ),
119  key="latest_pull_request",
120  translation_key="latest_pull_request",
121  avabl_fn=lambda data: data["pull_request"]["pull_requests"],
122  value_fn=lambda data: data["pull_request"]["pull_requests"][0]["title"][:255],
123  attr_fn=lambda data: {
124  "url": data["pull_request"]["pull_requests"][0]["url"],
125  "number": data["pull_request"]["pull_requests"][0]["number"],
126  },
127  ),
129  key="latest_tag",
130  translation_key="latest_tag",
131  avabl_fn=lambda data: data["refs"]["tags"],
132  value_fn=lambda data: data["refs"]["tags"][0]["name"][:255],
133  attr_fn=lambda data: {
134  "url": data["refs"]["tags"][0]["target"]["url"],
135  },
136  ),
137 )
138 
139 
141  hass: HomeAssistant,
142  entry: GithubConfigEntry,
143  async_add_entities: AddEntitiesCallback,
144 ) -> None:
145  """Set up GitHub sensor based on a config entry."""
146  repositories = entry.runtime_data
148  (
149  GitHubSensorEntity(coordinator, description)
150  for description in SENSOR_DESCRIPTIONS
151  for coordinator in repositories.values()
152  ),
153  )
154 
155 
156 class GitHubSensorEntity(CoordinatorEntity[GitHubDataUpdateCoordinator], SensorEntity):
157  """Defines a GitHub sensor entity."""
158 
159  _attr_attribution = "Data provided by the GitHub API"
160  _attr_has_entity_name = True
161 
162  entity_description: GitHubSensorEntityDescription
163 
164  def __init__(
165  self,
166  coordinator: GitHubDataUpdateCoordinator,
167  entity_description: GitHubSensorEntityDescription,
168  ) -> None:
169  """Initialize the sensor."""
170  super().__init__(coordinator=coordinator)
171 
172  self.entity_descriptionentity_description = entity_description
173  self._attr_unique_id_attr_unique_id = f"{coordinator.data.get('id')}_{entity_description.key}"
174 
175  self._attr_device_info_attr_device_info = DeviceInfo(
176  identifiers={(DOMAIN, coordinator.repository)},
177  name=coordinator.data.get("full_name"),
178  manufacturer="GitHub",
179  configuration_url=f"https://github.com/{coordinator.repository}",
180  entry_type=DeviceEntryType.SERVICE,
181  )
182 
183  @property
184  def available(self) -> bool:
185  """Return True if entity is available."""
186  return (
187  super().available
188  and self.coordinator.data is not None
189  and self.entity_descriptionentity_description.avabl_fn(self.coordinator.data)
190  )
191 
192  @property
193  def native_value(self) -> StateType:
194  """Return the state of the sensor."""
195  return self.entity_descriptionentity_description.value_fn(self.coordinator.data)
196 
197  @property
198  def extra_state_attributes(self) -> Mapping[str, Any] | None:
199  """Return the extra state attributes."""
200  return self.entity_descriptionentity_description.attr_fn(self.coordinator.data)
None __init__(self, GitHubDataUpdateCoordinator coordinator, GitHubSensorEntityDescription entity_description)
Definition: sensor.py:168
Mapping[str, Any]|None extra_state_attributes(self)
Definition: sensor.py:198
None async_setup_entry(HomeAssistant hass, GithubConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:144