Home Assistant Unofficial Reference 2024.12.1
geo_location.py
Go to the documentation of this file.
1 """Geolocation support for GDACS Feed."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from datetime import datetime
7 import logging
8 from typing import Any
9 
10 from aio_georss_gdacs.feed_entry import GdacsFeedEntry
11 
12 from homeassistant.components.geo_location import GeolocationEvent
13 from homeassistant.config_entries import ConfigEntry
14 from homeassistant.const import UnitOfLength
15 from homeassistant.core import HomeAssistant, callback
16 from homeassistant.helpers import entity_registry as er
17 from homeassistant.helpers.dispatcher import async_dispatcher_connect
18 from homeassistant.helpers.entity_platform import AddEntitiesCallback
19 from homeassistant.util.unit_conversion import DistanceConverter
20 from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
21 
22 from . import GdacsFeedEntityManager
23 from .const import DEFAULT_ICON, DOMAIN, FEED
24 
25 _LOGGER = logging.getLogger(__name__)
26 
27 ATTR_ALERT_LEVEL = "alert_level"
28 ATTR_COUNTRY = "country"
29 ATTR_DESCRIPTION = "description"
30 ATTR_DURATION_IN_WEEK = "duration_in_week"
31 ATTR_EVENT_TYPE = "event_type"
32 ATTR_EXTERNAL_ID = "external_id"
33 ATTR_FROM_DATE = "from_date"
34 ATTR_POPULATION = "population"
35 ATTR_SEVERITY = "severity"
36 ATTR_TO_DATE = "to_date"
37 ATTR_VULNERABILITY = "vulnerability"
38 
39 ICONS = {
40  "DR": "mdi:water-off",
41  "EQ": "mdi:pulse",
42  "FL": "mdi:home-flood",
43  "TC": "mdi:weather-hurricane",
44  "TS": "mdi:waves",
45  "VO": "mdi:image-filter-hdr",
46 }
47 
48 # An update of this entity is not making a web request, but uses internal data only.
49 PARALLEL_UPDATES = 0
50 
51 SOURCE = "gdacs"
52 
53 
55  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
56 ) -> None:
57  """Set up the GDACS Feed platform."""
58  manager: GdacsFeedEntityManager = hass.data[DOMAIN][FEED][entry.entry_id]
59 
60  @callback
61  def async_add_geolocation(
62  feed_manager: GdacsFeedEntityManager, integration_id: str, external_id: str
63  ) -> None:
64  """Add geolocation entity from feed."""
65  new_entity = GdacsEvent(feed_manager, integration_id, external_id)
66  _LOGGER.debug("Adding geolocation %s", new_entity)
67  async_add_entities([new_entity], True)
68 
69  manager.listeners.append(
71  hass, manager.async_event_new_entity(), async_add_geolocation
72  )
73  )
74  # Do not wait for update here so that the setup can be completed and because an
75  # update will fetch data from the feed via HTTP and then process that data.
76  hass.async_create_task(manager.async_update())
77  _LOGGER.debug("Geolocation setup done")
78 
79 
81  """Represents an external event with GDACS feed data."""
82 
83  _attr_should_poll = False
84  _attr_source = SOURCE
85 
86  def __init__(
87  self,
88  feed_manager: GdacsFeedEntityManager,
89  integration_id: str,
90  external_id: str,
91  ) -> None:
92  """Initialize entity with data from feed entry."""
93  self._feed_manager_feed_manager = feed_manager
94  self._external_id_external_id = external_id
95  self._attr_unique_id_attr_unique_id = f"{integration_id}_{external_id}"
96  self._attr_unit_of_measurement_attr_unit_of_measurement = UnitOfLength.KILOMETERS
97  self._alert_level_alert_level: str | None = None
98  self._country_country: str | None = None
99  self._description_description: str | None = None
100  self._duration_in_week_duration_in_week: int | None = None
101  self._event_type_short_event_type_short: str | None = None
102  self._event_type_event_type: str | None = None
103  self._from_date_from_date: datetime | None = None
104  self._to_date_to_date: datetime | None = None
105  self._population_population: str | None = None
106  self._severity_severity: str | None = None
107  self._vulnerability_vulnerability: str | float | None = None
108  self._version_version: int | None = None
109  self._remove_signal_delete_remove_signal_delete: Callable[[], None]
110  self._remove_signal_update_remove_signal_update: Callable[[], None]
111 
112  async def async_added_to_hass(self) -> None:
113  """Call when entity is added to hass."""
114  if self.hasshass.config.units is US_CUSTOMARY_SYSTEM:
115  self._attr_unit_of_measurement_attr_unit_of_measurement = UnitOfLength.MILES
116  self._remove_signal_delete_remove_signal_delete = async_dispatcher_connect(
117  self.hasshass, f"gdacs_delete_{self._external_id}", self._delete_callback_delete_callback
118  )
119  self._remove_signal_update_remove_signal_update = async_dispatcher_connect(
120  self.hasshass, f"gdacs_update_{self._external_id}", self._update_callback_update_callback
121  )
122 
123  async def async_will_remove_from_hass(self) -> None:
124  """Call when entity will be removed from hass."""
125  self._remove_signal_delete_remove_signal_delete()
126  self._remove_signal_update_remove_signal_update()
127  # Remove from entity registry.
128  entity_registry = er.async_get(self.hasshass)
129  if self.entity_identity_id in entity_registry.entities:
130  entity_registry.async_remove(self.entity_identity_id)
131 
132  @callback
133  def _delete_callback(self) -> None:
134  """Remove this entity."""
135  self.hasshass.async_create_task(self.async_removeasync_remove(force_remove=True))
136 
137  @callback
138  def _update_callback(self) -> None:
139  """Call update method."""
140  self.async_schedule_update_ha_stateasync_schedule_update_ha_state(True)
141 
142  async def async_update(self) -> None:
143  """Update this entity from the data held in the feed manager."""
144  _LOGGER.debug("Updating %s", self._external_id_external_id)
145  feed_entry = self._feed_manager_feed_manager.get_entry(self._external_id_external_id)
146  if feed_entry:
147  self._update_from_feed_update_from_feed(feed_entry)
148 
149  def _update_from_feed(self, feed_entry: GdacsFeedEntry) -> None:
150  """Update the internal state from the provided feed entry."""
151  if not (event_name := feed_entry.event_name):
152  # Earthquakes usually don't have an event name.
153  event_name = f"{feed_entry.country} ({feed_entry.event_id})"
154  self._attr_name_attr_name = f"{feed_entry.event_type}: {event_name}"
155  # Convert distance if not metric system.
156  if self.hasshass.config.units is US_CUSTOMARY_SYSTEM:
157  self._attr_distance_attr_distance = DistanceConverter.convert(
158  feed_entry.distance_to_home, UnitOfLength.KILOMETERS, UnitOfLength.MILES
159  )
160  else:
161  self._attr_distance_attr_distance = feed_entry.distance_to_home
162  self._attr_latitude_attr_latitude = feed_entry.coordinates[0]
163  self._attr_longitude_attr_longitude = feed_entry.coordinates[1]
164  self._attr_attribution_attr_attribution = feed_entry.attribution
165  self._alert_level_alert_level = feed_entry.alert_level
166  self._country_country = feed_entry.country
167  self._description_description = feed_entry.title
168  self._duration_in_week_duration_in_week = feed_entry.duration_in_week
169  self._event_type_short_event_type_short = feed_entry.event_type_short
170  self._event_type_event_type = feed_entry.event_type
171  self._from_date_from_date = feed_entry.from_date
172  self._to_date_to_date = feed_entry.to_date
173  self._population_population = feed_entry.population
174  self._severity_severity = feed_entry.severity
175  self._vulnerability_vulnerability = feed_entry.vulnerability
176  # Round vulnerability value if presented as float.
177  if isinstance(self._vulnerability_vulnerability, float):
178  self._vulnerability_vulnerability = round(self._vulnerability_vulnerability, 1)
179  self._version_version = feed_entry.version
180 
181  @property
182  def icon(self) -> str:
183  """Return the icon to use in the frontend, if any."""
184  if self._event_type_short_event_type_short and self._event_type_short_event_type_short in ICONS:
185  return ICONS[self._event_type_short_event_type_short]
186  return DEFAULT_ICON
187 
188  @property
189  def extra_state_attributes(self) -> dict[str, Any]:
190  """Return the device state attributes."""
191  return {
192  key: value
193  for key, value in (
194  (ATTR_EXTERNAL_ID, self._external_id_external_id),
195  (ATTR_DESCRIPTION, self._description_description),
196  (ATTR_EVENT_TYPE, self._event_type_event_type),
197  (ATTR_ALERT_LEVEL, self._alert_level_alert_level),
198  (ATTR_COUNTRY, self._country_country),
199  (ATTR_DURATION_IN_WEEK, self._duration_in_week_duration_in_week),
200  (ATTR_FROM_DATE, self._from_date_from_date),
201  (ATTR_TO_DATE, self._to_date_to_date),
202  (ATTR_POPULATION, self._population_population),
203  (ATTR_SEVERITY, self._severity_severity),
204  (ATTR_VULNERABILITY, self._vulnerability_vulnerability),
205  )
206  if value or isinstance(value, bool)
207  }
None _update_from_feed(self, GdacsFeedEntry feed_entry)
None __init__(self, GdacsFeedEntityManager feed_manager, str integration_id, str external_id)
Definition: geo_location.py:91
None async_schedule_update_ha_state(self, bool force_refresh=False)
Definition: entity.py:1265
None async_remove(self, *bool force_remove=False)
Definition: entity.py:1387
config_entries.ConfigEntry|None get_entry(HomeAssistant hass, websocket_api.ActiveConnection connection, str entry_id, int msg_id)
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: geo_location.py:56
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103