Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Launch Library sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import datetime
8 from typing import Any
9 
10 from pylaunches.types import Event, Launch
11 
13  SensorDeviceClass,
14  SensorEntity,
15  SensorEntityDescription,
16 )
17 from homeassistant.config_entries import ConfigEntry
18 from homeassistant.const import CONF_NAME, PERCENTAGE
19 from homeassistant.core import HomeAssistant, callback
20 from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
21 from homeassistant.helpers.entity_platform import AddEntitiesCallback
23  CoordinatorEntity,
24  DataUpdateCoordinator,
25 )
26 from homeassistant.util.dt import parse_datetime
27 
28 from . import LaunchLibraryData
29 from .const import DOMAIN
30 
31 DEFAULT_NEXT_LAUNCH_NAME = "Next launch"
32 
33 
34 @dataclass(frozen=True, kw_only=True)
36  """Describes a Next Launch sensor entity."""
37 
38  value_fn: Callable[[Launch | Event], datetime | int | str | None]
39  attributes_fn: Callable[[Launch | Event], dict[str, Any] | None]
40 
41 
42 SENSOR_DESCRIPTIONS: tuple[LaunchLibrarySensorEntityDescription, ...] = (
44  key="next_launch",
45  icon="mdi:rocket-launch",
46  translation_key="next_launch",
47  value_fn=lambda nl: nl["name"],
48  attributes_fn=lambda nl: {
49  "provider": nl["launch_service_provider"]["name"],
50  "pad": nl["pad"]["name"],
51  "facility": nl["pad"]["location"]["name"],
52  "provider_country_code": nl["pad"]["location"]["country_code"],
53  },
54  ),
56  key="launch_time",
57  icon="mdi:clock-outline",
58  translation_key="launch_time",
59  device_class=SensorDeviceClass.TIMESTAMP,
60  value_fn=lambda nl: parse_datetime(nl["net"]),
61  attributes_fn=lambda nl: {
62  "window_start": nl["window_start"],
63  "window_end": nl["window_end"],
64  "stream_live": nl["window_start"],
65  },
66  ),
68  key="launch_probability",
69  icon="mdi:dice-multiple",
70  translation_key="launch_probability",
71  native_unit_of_measurement=PERCENTAGE,
72  value_fn=lambda nl: None if nl["probability"] == -1 else nl["probability"],
73  attributes_fn=lambda nl: None,
74  ),
76  key="launch_status",
77  icon="mdi:rocket-launch",
78  translation_key="launch_status",
79  value_fn=lambda nl: nl["status"]["name"],
80  attributes_fn=lambda nl: {"reason": nl.get("holdreason")},
81  ),
83  key="launch_mission",
84  icon="mdi:orbit",
85  translation_key="launch_mission",
86  value_fn=lambda nl: nl["mission"]["name"],
87  attributes_fn=lambda nl: {
88  "mission_type": nl["mission"]["type"],
89  "target_orbit": nl["mission"]["orbit"]["name"],
90  "description": nl["mission"]["description"],
91  },
92  ),
94  key="starship_launch",
95  icon="mdi:rocket",
96  translation_key="starship_launch",
97  device_class=SensorDeviceClass.TIMESTAMP,
98  value_fn=lambda sl: parse_datetime(sl["net"]),
99  attributes_fn=lambda sl: {
100  "title": sl["mission"]["name"],
101  "status": sl["status"]["name"],
102  "target_orbit": sl["mission"]["orbit"]["name"],
103  "description": sl["mission"]["description"],
104  },
105  ),
107  key="starship_event",
108  icon="mdi:calendar",
109  translation_key="starship_event",
110  device_class=SensorDeviceClass.TIMESTAMP,
111  value_fn=lambda se: parse_datetime(se["date"]),
112  attributes_fn=lambda se: {
113  "title": se["name"],
114  "location": se["location"],
115  "stream": se["video_url"],
116  "description": se["description"],
117  },
118  ),
119 )
120 
121 
123  hass: HomeAssistant,
124  entry: ConfigEntry,
125  async_add_entities: AddEntitiesCallback,
126 ) -> None:
127  """Set up the sensor platform."""
128  name = entry.data.get(CONF_NAME, DEFAULT_NEXT_LAUNCH_NAME)
129  coordinator: DataUpdateCoordinator[LaunchLibraryData] = hass.data[DOMAIN]
130 
133  coordinator=coordinator,
134  entry_id=entry.entry_id,
135  description=description,
136  name=name,
137  )
138  for description in SENSOR_DESCRIPTIONS
139  )
140 
141 
143  CoordinatorEntity[DataUpdateCoordinator[LaunchLibraryData]], SensorEntity
144 ):
145  """Representation of the next launch sensors."""
146 
147  _attr_attribution = "Data provided by Launch Library."
148  _attr_has_entity_name = True
149  _next_event: Launch | Event | None = None
150  entity_description: LaunchLibrarySensorEntityDescription
151 
152  def __init__(
153  self,
154  coordinator: DataUpdateCoordinator[LaunchLibraryData],
155  entry_id: str,
156  description: LaunchLibrarySensorEntityDescription,
157  name: str,
158  ) -> None:
159  """Initialize a Launch Library sensor."""
160  super().__init__(coordinator)
161  self._attr_unique_id_attr_unique_id = f"{entry_id}_{description.key}"
162  self.entity_descriptionentity_description = description
163  self._attr_device_info_attr_device_info = DeviceInfo(
164  identifiers={(DOMAIN, entry_id)},
165  entry_type=DeviceEntryType.SERVICE,
166  name=name,
167  )
168 
169  @property
170  def native_value(self) -> datetime | str | int | None:
171  """Return the state of the sensor."""
172  if self._next_event_next_event is None:
173  return None
174  return self.entity_descriptionentity_description.value_fn(self._next_event_next_event)
175 
176  @property
177  def extra_state_attributes(self) -> dict[str, Any] | None:
178  """Return the attributes of the sensor."""
179  if self._next_event_next_event is None:
180  return None
181  return self.entity_descriptionentity_description.attributes_fn(self._next_event_next_event)
182 
183  @property
184  def available(self) -> bool:
185  """Return if the sensor is available."""
186  return super().available and self._next_event_next_event is not None
187 
188  @callback
189  def _handle_coordinator_update(self) -> None:
190  """Handle updated data from the coordinator."""
191  if self.entity_descriptionentity_description.key == "starship_launch":
192  events = self.coordinator.data["starship_events"]["upcoming"]["launches"]
193  elif self.entity_descriptionentity_description.key == "starship_event":
194  events = self.coordinator.data["starship_events"]["upcoming"]["events"]
195  else:
196  events = self.coordinator.data["upcoming_launches"]
197 
198  self._next_event_next_event = next((event for event in (events)), None)
200 
201  async def async_added_to_hass(self) -> None:
202  """When entity is added to hass."""
203  await super().async_added_to_hass()
204  self._handle_coordinator_update_handle_coordinator_update()
None __init__(self, DataUpdateCoordinator[LaunchLibraryData] coordinator, str entry_id, LaunchLibrarySensorEntityDescription description, str name)
Definition: sensor.py:158
datetime|None parse_datetime(str|None value)
Definition: sensor.py:138
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:126