1 """The HERE Travel Time integration."""
3 from __future__
import annotations
5 from datetime
import datetime, time, timedelta
10 from here_routing
import (
12 HERERoutingTooManyRequestsError,
19 from here_transit
import (
21 HERETransitConnectionError,
22 HERETransitDepartureArrivalTooCloseError,
23 HERETransitNoRouteFoundError,
24 HERETransitTooManyRequestsError,
26 import voluptuous
as vol
36 from .const
import DEFAULT_SCAN_INTERVAL, DOMAIN, ROUTE_MODE_FASTEST
37 from .model
import HERETravelTimeConfig, HERETravelTimeData
39 BACKOFF_MULTIPLIER = 1.1
41 _LOGGER = logging.getLogger(__name__)
45 """here_routing DataUpdateCoordinator."""
51 config: HERETravelTimeConfig,
58 update_interval=
timedelta(seconds=DEFAULT_SCAN_INTERVAL),
60 self.
_api_api = HERERoutingApi(api_key)
64 """Get the latest data from the HERE Routing API."""
71 if self.
configconfig.route_mode == ROUTE_MODE_FASTEST
72 else RoutingMode.SHORT
77 "Requesting route for origin: %s, destination: %s, route_mode: %s,"
78 " mode: %s, arrival: %s, departure: %s"
83 TransportMode(self.
configconfig.travel_mode),
89 response = await self.
_api_api.route(
90 transport_mode=TransportMode(self.
configconfig.travel_mode),
91 origin=here_routing.Place(origin[0], origin[1]),
92 destination=here_routing.Place(destination[0], destination[1]),
93 routing_mode=route_mode,
95 departure_time=departure,
96 return_values=[Return.POLYINE, Return.SUMMARY],
99 except HERERoutingTooManyRequestsError
as error:
102 "Rate limit has been reached. Increasing update interval to %s",
108 raise UpdateFailed(
"Rate limit has been reached")
from error
109 _LOGGER.debug(
"Raw response is: %s", response)
113 "Resetting update interval to %s",
114 DEFAULT_SCAN_INTERVAL,
120 """Parse the routing response dict to a HERETravelTimeData."""
121 distance: float = 0.0
122 duration: float = 0.0
123 duration_in_traffic: float = 0.0
125 for section
in response[
"routes"][0][
"sections"]:
126 distance += DistanceConverter.convert(
127 section[
"summary"][
"length"],
129 UnitOfLength.KILOMETERS,
131 duration += section[
"summary"][
"baseDuration"]
132 duration_in_traffic += section[
"summary"][
"duration"]
134 first_section = response[
"routes"][0][
"sections"][0]
135 last_section = response[
"routes"][0][
"sections"][-1]
136 mapped_origin_lat: float = first_section[
"departure"][
"place"][
"location"][
139 mapped_origin_lon: float = first_section[
"departure"][
"place"][
"location"][
142 mapped_destination_lat: float = last_section[
"arrival"][
"place"][
"location"][
145 mapped_destination_lon: float = last_section[
"arrival"][
"place"][
"location"][
148 origin_name: str |
None =
None
149 if (names := first_section[
"spans"][0].
get(
"names"))
is not None:
150 origin_name = names[0][
"value"]
151 destination_name: str |
None =
None
152 if (names := last_section[
"spans"][-1].
get(
"names"))
is not None:
153 destination_name = names[0][
"value"]
156 duration=round(duration / 60),
157 duration_in_traffic=round(duration_in_traffic / 60),
159 origin=f
"{mapped_origin_lat},{mapped_origin_lon}",
160 destination=f
"{mapped_destination_lat},{mapped_destination_lon}",
161 origin_name=origin_name,
162 destination_name=destination_name,
167 DataUpdateCoordinator[HERETravelTimeData |
None]
169 """HERETravelTime DataUpdateCoordinator."""
175 config: HERETravelTimeConfig,
182 update_interval=
timedelta(seconds=DEFAULT_SCAN_INTERVAL),
184 self.
_api_api = HERETransitApi(api_key)
188 """Get the latest data from the HERE Routing API."""
195 "Requesting transit route for origin: %s, destination: %s, arrival: %s,"
204 response = await self.
_api_api.route(
205 origin=here_transit.Place(latitude=origin[0], longitude=origin[1]),
206 destination=here_transit.Place(
207 latitude=destination[0], longitude=destination[1]
209 arrival_time=arrival,
210 departure_time=departure,
212 here_transit.Return.POLYLINE,
213 here_transit.Return.TRAVEL_SUMMARY,
216 except HERETransitTooManyRequestsError
as error:
219 "Rate limit has been reached. Increasing update interval to %s",
225 raise UpdateFailed(
"Rate limit has been reached")
from error
226 except HERETransitDepartureArrivalTooCloseError:
227 _LOGGER.debug(
"Ignoring HERETransitDepartureArrivalTooCloseError")
229 except (HERETransitConnectionError, HERETransitNoRouteFoundError)
as error:
230 raise UpdateFailed
from error
232 _LOGGER.debug(
"Raw response is: %s", response)
235 "Resetting update interval to %s",
236 DEFAULT_SCAN_INTERVAL,
242 """Parse the transit response dict to a HERETravelTimeData."""
243 sections: list[dict[str, Any]] = response[
"routes"][0][
"sections"]
245 mapped_origin_lat: float = sections[0][
"departure"][
"place"][
"location"][
"lat"]
246 mapped_origin_lon: float = sections[0][
"departure"][
"place"][
"location"][
"lng"]
247 mapped_destination_lat: float = sections[-1][
"arrival"][
"place"][
"location"][
250 mapped_destination_lon: float = sections[-1][
"arrival"][
"place"][
"location"][
253 distance: float = DistanceConverter.convert(
254 sum(section[
"travelSummary"][
"length"]
for section
in sections),
256 UnitOfLength.KILOMETERS,
258 duration: float = sum(
259 section[
"travelSummary"][
"duration"]
for section
in sections
262 attribution=attribution,
263 duration=round(duration / 60),
264 duration_in_traffic=round(duration / 60),
266 origin=f
"{mapped_origin_lat},{mapped_origin_lon}",
267 destination=f
"{mapped_destination_lat},{mapped_destination_lon}",
268 origin_name=sections[0][
"departure"][
"place"].
get(
"name"),
269 destination_name=sections[-1][
"arrival"][
"place"].
get(
"name"),
275 config: HERETravelTimeConfig,
276 ) -> tuple[list[str], list[str], str |
None, str |
None]:
277 """Prepare parameters for the HERE api."""
279 def _from_entity_id(entity_id: str) -> list[str]:
281 if coordinates
is None:
282 raise UpdateFailed(f
"No coordinates found for {entity_id}")
283 if coordinates
is entity_id:
284 raise UpdateFailed(f
"Could not find entity {entity_id}")
286 formatted_coordinates = coordinates.split(
",")
287 vol.Schema(cv.gps(formatted_coordinates))
288 except (AttributeError, vol.ExactSequenceInvalid)
as ex:
290 f
"{entity_id} does not have valid coordinates: {coordinates}"
292 return formatted_coordinates
295 if config.destination_entity_id
is not None:
296 destination = _from_entity_id(config.destination_entity_id)
299 str(config.destination_latitude),
300 str(config.destination_longitude),
304 if config.origin_entity_id
is not None:
305 origin = _from_entity_id(config.origin_entity_id)
308 str(config.origin_latitude),
309 str(config.origin_longitude),
313 arrival: str |
None =
None
314 departure: str |
None =
None
315 if config.arrival
is not None:
317 if config.departure
is not None:
320 return (origin, destination, arrival, departure)
324 """Build a hass frontend ready string out of the attributions."""
325 relevant_attributions = []
326 for section
in sections:
327 if (attributions := section.get(
"attributions"))
is not None:
328 for attribution
in attributions:
329 if (href := attribution.get(
"href"))
is not None:
330 relevant_attributions.append(f
"{href}")
331 if (text := attribution.get(
"text"))
is not None:
332 relevant_attributions.append(text)
333 if len(relevant_attributions) > 0:
334 return ",".join(relevant_attributions)
339 """Take a time like 08:00:00 and combine it with the current date."""
340 combined = datetime.combine(dt_util.start_of_local_day(), simple_time)
341 if combined < datetime.now():
None __init__(self, HomeAssistant hass, str api_key, HERETravelTimeConfig config)
HERETravelTimeData _async_update_data(self)
HERETravelTimeData _parse_routing_response(self, dict[str, Any] response)
HERETravelTimeData _parse_transit_response(self, dict[str, Any] response)
None __init__(self, HomeAssistant hass, str api_key, HERETravelTimeConfig config)
HERETravelTimeData|None _async_update_data(self)
None update_interval(self, timedelta|None value)
timedelta|None update_interval(self)
web.Response get(self, web.Request request, str config_key)
tuple[list[str], list[str], str|None, str|None] prepare_parameters(HomeAssistant hass, HERETravelTimeConfig config)
datetime next_datetime(time simple_time)
str|None build_hass_attribution(list[dict[str, Any]] sections)
str|None find_coordinates(HomeAssistant hass, str name, list|None recursion_history=None)