Home Assistant Unofficial Reference 2024.12.1
geo_location.py
Go to the documentation of this file.
1 """Support for IGN Sismologia (Earthquakes) Feeds."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from datetime import timedelta
7 import logging
8 from typing import Any
9 
10 from georss_ign_sismologia_client import (
11  IgnSismologiaFeedEntry,
12  IgnSismologiaFeedManager,
13 )
14 import voluptuous as vol
15 
17  PLATFORM_SCHEMA as GEO_LOCATION_PLATFORM_SCHEMA,
18  GeolocationEvent,
19 )
20 from homeassistant.const import (
21  CONF_LATITUDE,
22  CONF_LONGITUDE,
23  CONF_RADIUS,
24  CONF_SCAN_INTERVAL,
25  EVENT_HOMEASSISTANT_START,
26  UnitOfLength,
27 )
28 from homeassistant.core import Event, HomeAssistant, callback
30 from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send
31 from homeassistant.helpers.entity_platform import AddEntitiesCallback
32 from homeassistant.helpers.event import track_time_interval
33 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
34 
35 _LOGGER = logging.getLogger(__name__)
36 
37 ATTR_EXTERNAL_ID = "external_id"
38 ATTR_IMAGE_URL = "image_url"
39 ATTR_MAGNITUDE = "magnitude"
40 ATTR_PUBLICATION_DATE = "publication_date"
41 ATTR_REGION = "region"
42 ATTR_TITLE = "title"
43 
44 CONF_MINIMUM_MAGNITUDE = "minimum_magnitude"
45 
46 DEFAULT_MINIMUM_MAGNITUDE = 0.0
47 DEFAULT_RADIUS_IN_KM = 50.0
48 
49 SCAN_INTERVAL = timedelta(minutes=5)
50 
51 SOURCE = "ign_sismologia"
52 
53 PLATFORM_SCHEMA = GEO_LOCATION_PLATFORM_SCHEMA.extend(
54  {
55  vol.Optional(CONF_LATITUDE): cv.latitude,
56  vol.Optional(CONF_LONGITUDE): cv.longitude,
57  vol.Optional(CONF_RADIUS, default=DEFAULT_RADIUS_IN_KM): vol.Coerce(float),
58  vol.Optional(
59  CONF_MINIMUM_MAGNITUDE, default=DEFAULT_MINIMUM_MAGNITUDE
60  ): cv.positive_float,
61  }
62 )
63 
64 
66  hass: HomeAssistant,
67  config: ConfigType,
68  add_entities: AddEntitiesCallback,
69  discovery_info: DiscoveryInfoType | None = None,
70 ) -> None:
71  """Set up the IGN Sismologia Feed platform."""
72  scan_interval: timedelta = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)
73  coordinates: tuple[float, float] = (
74  config.get(CONF_LATITUDE, hass.config.latitude),
75  config.get(CONF_LONGITUDE, hass.config.longitude),
76  )
77  radius_in_km: float = config[CONF_RADIUS]
78  minimum_magnitude: float = config[CONF_MINIMUM_MAGNITUDE]
79  # Initialize the entity manager.
81  hass, add_entities, scan_interval, coordinates, radius_in_km, minimum_magnitude
82  )
83 
84  def start_feed_manager(event: Event) -> None:
85  """Start feed manager."""
86  feed.startup()
87 
88  hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_feed_manager)
89 
90 
92  """Feed Entity Manager for IGN Sismologia GeoRSS feed."""
93 
94  def __init__(
95  self,
96  hass: HomeAssistant,
97  add_entities: AddEntitiesCallback,
98  scan_interval: timedelta,
99  coordinates: tuple[float, float],
100  radius_in_km: float,
101  minimum_magnitude: float,
102  ) -> None:
103  """Initialize the Feed Entity Manager."""
104 
105  self._hass_hass = hass
106  self._feed_manager_feed_manager = IgnSismologiaFeedManager(
107  self._generate_entity_generate_entity,
108  self._update_entity_update_entity,
109  self._remove_entity_remove_entity,
110  coordinates,
111  filter_radius=radius_in_km,
112  filter_minimum_magnitude=minimum_magnitude,
113  )
114  self._add_entities_add_entities = add_entities
115  self._scan_interval_scan_interval = scan_interval
116 
117  def startup(self) -> None:
118  """Start up this manager."""
119  self._feed_manager_feed_manager.update()
120  self._init_regular_updates_init_regular_updates()
121 
122  def _init_regular_updates(self) -> None:
123  """Schedule regular updates at the specified interval."""
125  self._hass_hass, lambda now: self._feed_manager_feed_manager.update(), self._scan_interval_scan_interval
126  )
127 
128  def get_entry(self, external_id: str) -> IgnSismologiaFeedEntry | None:
129  """Get feed entry by external id."""
130  return self._feed_manager_feed_manager.feed_entries.get(external_id)
131 
132  def _generate_entity(self, external_id: str) -> None:
133  """Generate new entity."""
134  new_entity = IgnSismologiaLocationEvent(self, external_id)
135  # Add new entities to HA.
136  self._add_entities_add_entities([new_entity], True)
137 
138  def _update_entity(self, external_id: str) -> None:
139  """Update entity."""
140  dispatcher_send(self._hass_hass, f"ign_sismologia_update_{external_id}")
141 
142  def _remove_entity(self, external_id: str) -> None:
143  """Remove entity."""
144  dispatcher_send(self._hass_hass, f"ign_sismologia_delete_{external_id}")
145 
146 
148  """Represents an external event with IGN Sismologia feed data."""
149 
150  _attr_icon = "mdi:pulse"
151  _attr_should_poll = False
152  _attr_source = SOURCE
153  _attr_unit_of_measurement = UnitOfLength.KILOMETERS
154 
155  def __init__(
156  self, feed_manager: IgnSismologiaFeedEntityManager, external_id: str
157  ) -> None:
158  """Initialize entity with data from feed entry."""
159  self._feed_manager_feed_manager = feed_manager
160  self._external_id_external_id = external_id
161  self._title_title = None
162  self._region_region = None
163  self._magnitude_magnitude = None
164  self._publication_date_publication_date = None
165  self._image_url_image_url = None
166  self._remove_signal_delete_remove_signal_delete: Callable[[], None]
167  self._remove_signal_update_remove_signal_update: Callable[[], None]
168 
169  async def async_added_to_hass(self) -> None:
170  """Call when entity is added to hass."""
171  self._remove_signal_delete_remove_signal_delete = async_dispatcher_connect(
172  self.hasshass,
173  f"ign_sismologia_delete_{self._external_id}",
174  self._delete_callback_delete_callback,
175  )
176  self._remove_signal_update_remove_signal_update = async_dispatcher_connect(
177  self.hasshass,
178  f"ign_sismologia_update_{self._external_id}",
179  self._update_callback_update_callback,
180  )
181 
182  @callback
183  def _delete_callback(self) -> None:
184  """Remove this entity."""
185  self._remove_signal_delete_remove_signal_delete()
186  self._remove_signal_update_remove_signal_update()
187  self.hasshass.async_create_task(self.async_removeasync_remove(force_remove=True))
188 
189  @callback
190  def _update_callback(self) -> None:
191  """Call update method."""
192  self.async_schedule_update_ha_stateasync_schedule_update_ha_state(True)
193 
194  async def async_update(self) -> None:
195  """Update this entity from the data held in the feed manager."""
196  _LOGGER.debug("Updating %s", self._external_id_external_id)
197  feed_entry = self._feed_manager_feed_manager.get_entry(self._external_id_external_id)
198  if feed_entry:
199  self._update_from_feed_update_from_feed(feed_entry)
200 
201  def _update_from_feed(self, feed_entry: IgnSismologiaFeedEntry) -> None:
202  """Update the internal state from the provided feed entry."""
203  self._title_title = feed_entry.title
204  self._attr_distance_attr_distance = feed_entry.distance_to_home
205  self._attr_latitude_attr_latitude = feed_entry.coordinates[0]
206  self._attr_longitude_attr_longitude = feed_entry.coordinates[1]
207  self._attr_attribution_attr_attribution = feed_entry.attribution
208  self._region_region = feed_entry.region
209  self._magnitude_magnitude = feed_entry.magnitude
210  self._publication_date_publication_date = feed_entry.published
211  self._image_url_image_url = feed_entry.image_url
212 
213  @property
214  def name(self) -> str | None:
215  """Return the name of the entity."""
216  if self._magnitude_magnitude and self._region_region:
217  return f"M {self._magnitude:.1f} - {self._region}"
218  if self._magnitude_magnitude:
219  return f"M {self._magnitude:.1f}"
220  if self._region_region:
221  return self._region_region
222  return self._title_title
223 
224  @property
225  def extra_state_attributes(self) -> dict[str, Any]:
226  """Return the device state attributes."""
227  return {
228  key: value
229  for key, value in (
230  (ATTR_EXTERNAL_ID, self._external_id_external_id),
231  (ATTR_TITLE, self._title_title),
232  (ATTR_REGION, self._region_region),
233  (ATTR_MAGNITUDE, self._magnitude_magnitude),
234  (ATTR_PUBLICATION_DATE, self._publication_date_publication_date),
235  (ATTR_IMAGE_URL, self._image_url_image_url),
236  )
237  if value or isinstance(value, bool)
238  }
None __init__(self, HomeAssistant hass, AddEntitiesCallback add_entities, timedelta scan_interval, tuple[float, float] coordinates, float radius_in_km, float minimum_magnitude)
None __init__(self, IgnSismologiaFeedEntityManager feed_manager, str external_id)
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 setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: geo_location.py:70
IssData update(pyiss.ISS iss)
Definition: __init__.py:33
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103
None dispatcher_send(HomeAssistant hass, str signal, *Any args)
Definition: dispatcher.py:137