Home Assistant Unofficial Reference 2024.12.1
geo_location.py
Go to the documentation of this file.
1 """Support for Queensland Bushfire Alert 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_qld_bushfire_alert_client import (
11  QldBushfireAlertFeedEntry,
12  QldBushfireAlertFeedManager,
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_CATEGORY = "category"
38 ATTR_EXTERNAL_ID = "external_id"
39 ATTR_PUBLICATION_DATE = "publication_date"
40 ATTR_STATUS = "status"
41 ATTR_UPDATED_DATE = "updated_date"
42 
43 CONF_CATEGORIES = "categories"
44 
45 DEFAULT_RADIUS_IN_KM = 20.0
46 
47 SCAN_INTERVAL = timedelta(minutes=5)
48 
49 SIGNAL_DELETE_ENTITY = "qld_bushfire_delete_{}"
50 SIGNAL_UPDATE_ENTITY = "qld_bushfire_update_{}"
51 
52 SOURCE = "qld_bushfire"
53 
54 VALID_CATEGORIES = [
55  "Emergency Warning",
56  "Watch and Act",
57  "Advice",
58  "Notification",
59  "Information",
60 ]
61 
62 PLATFORM_SCHEMA = GEO_LOCATION_PLATFORM_SCHEMA.extend(
63  {
64  vol.Optional(CONF_LATITUDE): cv.latitude,
65  vol.Optional(CONF_LONGITUDE): cv.longitude,
66  vol.Optional(CONF_RADIUS, default=DEFAULT_RADIUS_IN_KM): vol.Coerce(float),
67  vol.Optional(CONF_CATEGORIES, default=[]): vol.All(
68  cv.ensure_list, [vol.In(VALID_CATEGORIES)]
69  ),
70  }
71 )
72 
73 
75  hass: HomeAssistant,
76  config: ConfigType,
77  add_entities: AddEntitiesCallback,
78  discovery_info: DiscoveryInfoType | None = None,
79 ) -> None:
80  """Set up the Queensland Bushfire Alert Feed platform."""
81  scan_interval: timedelta = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)
82  coordinates: tuple[float, float] = (
83  config.get(CONF_LATITUDE, hass.config.latitude),
84  config.get(CONF_LONGITUDE, hass.config.longitude),
85  )
86  radius_in_km: float = config[CONF_RADIUS]
87  categories: list[str] = config[CONF_CATEGORIES]
88  # Initialize the entity manager.
90  hass, add_entities, scan_interval, coordinates, radius_in_km, categories
91  )
92 
93  def start_feed_manager(event: Event) -> None:
94  """Start feed manager."""
95  feed.startup()
96 
97  hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_feed_manager)
98 
99 
101  """Feed Entity Manager for Qld Bushfire Alert GeoRSS feed."""
102 
103  def __init__(
104  self,
105  hass: HomeAssistant,
106  add_entities: AddEntitiesCallback,
107  scan_interval: timedelta,
108  coordinates: tuple[float, float],
109  radius_in_km: float,
110  categories: list[str],
111  ) -> None:
112  """Initialize the Feed Entity Manager."""
113  self._hass_hass = hass
114  self._feed_manager_feed_manager = QldBushfireAlertFeedManager(
115  self._generate_entity_generate_entity,
116  self._update_entity_update_entity,
117  self._remove_entity_remove_entity,
118  coordinates,
119  filter_radius=radius_in_km,
120  filter_categories=categories,
121  )
122  self._add_entities_add_entities = add_entities
123  self._scan_interval_scan_interval = scan_interval
124 
125  def startup(self) -> None:
126  """Start up this manager."""
127  self._feed_manager_feed_manager.update()
128  self._init_regular_updates_init_regular_updates()
129 
130  def _init_regular_updates(self) -> None:
131  """Schedule regular updates at the specified interval."""
133  self._hass_hass,
134  lambda now: self._feed_manager_feed_manager.update(),
135  self._scan_interval_scan_interval,
136  cancel_on_shutdown=True,
137  )
138 
139  def get_entry(self, external_id: str) -> QldBushfireAlertFeedEntry | None:
140  """Get feed entry by external id."""
141  return self._feed_manager_feed_manager.feed_entries.get(external_id)
142 
143  def _generate_entity(self, external_id: str) -> None:
144  """Generate new entity."""
145  new_entity = QldBushfireLocationEvent(self, external_id)
146  # Add new entities to HA.
147  self._add_entities_add_entities([new_entity], True)
148 
149  def _update_entity(self, external_id: str) -> None:
150  """Update entity."""
151  dispatcher_send(self._hass_hass, SIGNAL_UPDATE_ENTITY.format(external_id))
152 
153  def _remove_entity(self, external_id: str) -> None:
154  """Remove entity."""
155  dispatcher_send(self._hass_hass, SIGNAL_DELETE_ENTITY.format(external_id))
156 
157 
159  """Represents an external event with Qld Bushfire feed data."""
160 
161  _attr_icon = "mdi:fire"
162  _attr_should_poll = False
163  _attr_source = SOURCE
164  _attr_unit_of_measurement = UnitOfLength.KILOMETERS
165 
166  def __init__(
167  self, feed_manager: QldBushfireFeedEntityManager, external_id: str
168  ) -> None:
169  """Initialize entity with data from feed entry."""
170  self._feed_manager_feed_manager = feed_manager
171  self._external_id_external_id = external_id
172  self._category_category = None
173  self._publication_date_publication_date = None
174  self._updated_date_updated_date = None
175  self._status_status = None
176  self._remove_signal_delete_remove_signal_delete: Callable[[], None]
177  self._remove_signal_update_remove_signal_update: Callable[[], None]
178 
179  async def async_added_to_hass(self) -> None:
180  """Call when entity is added to hass."""
181  self._remove_signal_delete_remove_signal_delete = async_dispatcher_connect(
182  self.hasshass,
183  SIGNAL_DELETE_ENTITY.format(self._external_id_external_id),
184  self._delete_callback_delete_callback,
185  )
186  self._remove_signal_update_remove_signal_update = async_dispatcher_connect(
187  self.hasshass,
188  SIGNAL_UPDATE_ENTITY.format(self._external_id_external_id),
189  self._update_callback_update_callback,
190  )
191 
192  @callback
193  def _delete_callback(self) -> None:
194  """Remove this entity."""
195  self._remove_signal_delete_remove_signal_delete()
196  self._remove_signal_update_remove_signal_update()
197  self.hasshass.async_create_task(self.async_removeasync_remove(force_remove=True))
198 
199  @callback
200  def _update_callback(self) -> None:
201  """Call update method."""
202  self.async_schedule_update_ha_stateasync_schedule_update_ha_state(True)
203 
204  async def async_update(self) -> None:
205  """Update this entity from the data held in the feed manager."""
206  _LOGGER.debug("Updating %s", self._external_id_external_id)
207  feed_entry = self._feed_manager_feed_manager.get_entry(self._external_id_external_id)
208  if feed_entry:
209  self._update_from_feed_update_from_feed(feed_entry)
210 
211  def _update_from_feed(self, feed_entry: QldBushfireAlertFeedEntry) -> None:
212  """Update the internal state from the provided feed entry."""
213  self._attr_name_attr_name = feed_entry.title
214  self._attr_distance_attr_distance = feed_entry.distance_to_home
215  self._attr_latitude_attr_latitude = feed_entry.coordinates[0]
216  self._attr_longitude_attr_longitude = feed_entry.coordinates[1]
217  self._attr_attribution_attr_attribution = feed_entry.attribution
218  self._category_category = feed_entry.category
219  self._publication_date_publication_date = feed_entry.published
220  self._updated_date_updated_date = feed_entry.updated
221  self._status_status = feed_entry.status
222 
223  @property
224  def extra_state_attributes(self) -> dict[str, Any]:
225  """Return the device state attributes."""
226  return {
227  key: value
228  for key, value in (
229  (ATTR_EXTERNAL_ID, self._external_id_external_id),
230  (ATTR_CATEGORY, self._category_category),
231  (ATTR_PUBLICATION_DATE, self._publication_date_publication_date),
232  (ATTR_UPDATED_DATE, self._updated_date_updated_date),
233  (ATTR_STATUS, self._status_status),
234  )
235  if value or isinstance(value, bool)
236  }
None __init__(self, HomeAssistant hass, AddEntitiesCallback add_entities, timedelta scan_interval, tuple[float, float] coordinates, float radius_in_km, list[str] categories)
QldBushfireAlertFeedEntry|None get_entry(self, str external_id)
None _update_from_feed(self, QldBushfireAlertFeedEntry feed_entry)
None __init__(self, QldBushfireFeedEntityManager 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)
IssData update(pyiss.ISS iss)
Definition: __init__.py:33
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: geo_location.py:79
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