Skip to content

Commit

Permalink
Add service waze_travel_time.get_travel_times (#108170)
Browse files Browse the repository at this point in the history
* Add service waze_travel_time.get_travel_times

* Align strings with home-assistant.io

* Remove not needed service args

* Use SelectSelectorConfig.sort

* Move vehicle_type mangling to async_get_travel_times
  • Loading branch information
eifinger committed May 8, 2024
1 parent 7923471 commit 3844e2d
Show file tree
Hide file tree
Showing 9 changed files with 365 additions and 74 deletions.
166 changes: 163 additions & 3 deletions homeassistant/components/waze_travel_time/__init__.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,184 @@
"""The waze_travel_time component."""

import asyncio
import logging

from pywaze.route_calculator import CalcRoutesResponse, WazeRouteCalculator, WRCError
import voluptuous as vol

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.const import CONF_REGION, Platform
from homeassistant.core import (
HomeAssistant,
ServiceCall,
ServiceResponse,
SupportsResponse,
)
from homeassistant.helpers.httpx_client import get_async_client
from homeassistant.helpers.selector import (
BooleanSelector,
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
TextSelector,
)

from .const import DOMAIN, SEMAPHORE
from .const import (
CONF_AVOID_FERRIES,
CONF_AVOID_SUBSCRIPTION_ROADS,
CONF_AVOID_TOLL_ROADS,
CONF_DESTINATION,
CONF_ORIGIN,
CONF_REALTIME,
CONF_UNITS,
CONF_VEHICLE_TYPE,
DEFAULT_VEHICLE_TYPE,
DOMAIN,
METRIC_UNITS,
REGIONS,
SEMAPHORE,
UNITS,
VEHICLE_TYPES,
)

PLATFORMS = [Platform.SENSOR]

SERVICE_GET_TRAVEL_TIMES = "get_travel_times"
SERVICE_GET_TRAVEL_TIMES_SCHEMA = vol.Schema(
{
vol.Required(CONF_ORIGIN): TextSelector(),
vol.Required(CONF_DESTINATION): TextSelector(),
vol.Required(CONF_REGION): SelectSelector(
SelectSelectorConfig(
options=REGIONS,
mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_REGION,
sort=True,
)
),
vol.Optional(CONF_REALTIME, default=False): BooleanSelector(),
vol.Optional(CONF_VEHICLE_TYPE, default=DEFAULT_VEHICLE_TYPE): SelectSelector(
SelectSelectorConfig(
options=VEHICLE_TYPES,
mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_VEHICLE_TYPE,
sort=True,
)
),
vol.Optional(CONF_UNITS, default=METRIC_UNITS): SelectSelector(
SelectSelectorConfig(
options=UNITS,
mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_UNITS,
sort=True,
)
),
vol.Optional(CONF_AVOID_TOLL_ROADS, default=False): BooleanSelector(),
vol.Optional(CONF_AVOID_SUBSCRIPTION_ROADS, default=False): BooleanSelector(),
vol.Optional(CONF_AVOID_FERRIES, default=False): BooleanSelector(),
}
)

_LOGGER = logging.getLogger(__name__)


async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Load the saved entities."""
if SEMAPHORE not in hass.data.setdefault(DOMAIN, {}):
hass.data.setdefault(DOMAIN, {})[SEMAPHORE] = asyncio.Semaphore(1)
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)

async def async_get_travel_times_service(service: ServiceCall) -> ServiceResponse:
httpx_client = get_async_client(hass)
client = WazeRouteCalculator(
region=service.data[CONF_REGION].upper(), client=httpx_client
)
response = await async_get_travel_times(
client=client,
origin=service.data[CONF_ORIGIN],
destination=service.data[CONF_DESTINATION],
vehicle_type=service.data[CONF_VEHICLE_TYPE],
avoid_toll_roads=service.data[CONF_AVOID_TOLL_ROADS],
avoid_subscription_roads=service.data[CONF_AVOID_SUBSCRIPTION_ROADS],
avoid_ferries=service.data[CONF_AVOID_FERRIES],
realtime=service.data[CONF_REALTIME],
)
return {"routes": [vars(route) for route in response]} if response else None

hass.services.async_register(
DOMAIN,
SERVICE_GET_TRAVEL_TIMES,
async_get_travel_times_service,
SERVICE_GET_TRAVEL_TIMES_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
return True


async def async_get_travel_times(
client: WazeRouteCalculator,
origin: str,
destination: str,
vehicle_type: str,
avoid_toll_roads: bool,
avoid_subscription_roads: bool,
avoid_ferries: bool,
realtime: bool,
incl_filter: str | None = None,
excl_filter: str | None = None,
) -> list[CalcRoutesResponse] | None:
"""Get all available routes."""

_LOGGER.debug(
"Getting update for origin: %s destination: %s",
origin,
destination,
)
routes = []
vehicle_type = "" if vehicle_type.upper() == "CAR" else vehicle_type.upper()
try:
routes = await client.calc_routes(
origin,
destination,
vehicle_type=vehicle_type,
avoid_toll_roads=avoid_toll_roads,
avoid_subscription_roads=avoid_subscription_roads,
avoid_ferries=avoid_ferries,
real_time=realtime,
alternatives=3,
)

if incl_filter not in {None, ""}:
routes = [
r
for r in routes
if any(
incl_filter.lower() == street_name.lower() # type: ignore[union-attr]
for street_name in r.street_names
)
]

if excl_filter not in {None, ""}:
routes = [
r
for r in routes
if not any(
excl_filter.lower() == street_name.lower() # type: ignore[union-attr]
for street_name in r.street_names
)
]

if len(routes) < 1:
_LOGGER.warning("No routes found")
return None
except WRCError as exp:
_LOGGER.warning("Error on retrieving data: %s", exp)
return None

else:
return routes


async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
9 changes: 6 additions & 3 deletions homeassistant/components/waze_travel_time/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,18 @@
vol.Optional(CONF_REALTIME): BooleanSelector(),
vol.Required(CONF_VEHICLE_TYPE): SelectSelector(
SelectSelectorConfig(
options=sorted(VEHICLE_TYPES),
options=VEHICLE_TYPES,
mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_VEHICLE_TYPE,
sort=True,
)
),
vol.Required(CONF_UNITS): SelectSelector(
SelectSelectorConfig(
options=sorted(UNITS),
options=UNITS,
mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_UNITS,
sort=True,
)
),
vol.Optional(CONF_AVOID_TOLL_ROADS): BooleanSelector(),
Expand All @@ -76,9 +78,10 @@
vol.Required(CONF_DESTINATION): TextSelector(),
vol.Required(CONF_REGION): SelectSelector(
SelectSelectorConfig(
options=sorted(REGIONS),
options=REGIONS,
mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_REGION,
sort=True,
)
),
}
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/waze_travel_time/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
"default": "mdi:car"
}
}
},
"services": {
"get_travel_times": "mdi:timelapse"
}
}
82 changes: 28 additions & 54 deletions homeassistant/components/waze_travel_time/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import Any

import httpx
from pywaze.route_calculator import WazeRouteCalculator, WRCError
from pywaze.route_calculator import WazeRouteCalculator

from homeassistant.components.sensor import (
SensorDeviceClass,
Expand All @@ -30,6 +30,7 @@
from homeassistant.helpers.location import find_coordinates
from homeassistant.util.unit_conversion import DistanceConverter

from . import async_get_travel_times
from .const import (
CONF_AVOID_FERRIES,
CONF_AVOID_SUBSCRIPTION_ROADS,
Expand Down Expand Up @@ -186,65 +187,38 @@ async def async_update(self):
excl_filter = self.config_entry.options.get(CONF_EXCL_FILTER)
realtime = self.config_entry.options[CONF_REALTIME]
vehicle_type = self.config_entry.options[CONF_VEHICLE_TYPE]
vehicle_type = "" if vehicle_type.upper() == "CAR" else vehicle_type.upper()
avoid_toll_roads = self.config_entry.options[CONF_AVOID_TOLL_ROADS]
avoid_subscription_roads = self.config_entry.options[
CONF_AVOID_SUBSCRIPTION_ROADS
]
avoid_ferries = self.config_entry.options[CONF_AVOID_FERRIES]
units = self.config_entry.options[CONF_UNITS]

routes = {}
try:
routes = await self.client.calc_routes(
self.origin,
self.destination,
vehicle_type=vehicle_type,
avoid_toll_roads=avoid_toll_roads,
avoid_subscription_roads=avoid_subscription_roads,
avoid_ferries=avoid_ferries,
real_time=realtime,
alternatives=3,
)

if incl_filter not in {None, ""}:
routes = [
r
for r in routes
if any(
incl_filter.lower() == street_name.lower()
for street_name in r.street_names
)
]

if excl_filter not in {None, ""}:
routes = [
r
for r in routes
if not any(
excl_filter.lower() == street_name.lower()
for street_name in r.street_names
)
]

if len(routes) < 1:
_LOGGER.warning("No routes found")
return

routes = await async_get_travel_times(
self.client,
self.origin,
self.destination,
vehicle_type,
avoid_toll_roads,
avoid_subscription_roads,
avoid_ferries,
realtime,
incl_filter,
excl_filter,
)
if routes:
route = routes[0]
else:
_LOGGER.warning("No routes found")
return

self.duration = route.duration
distance = route.distance
self.duration = route.duration
distance = route.distance

if units == IMPERIAL_UNITS:
# Convert to miles.
self.distance = DistanceConverter.convert(
distance, UnitOfLength.KILOMETERS, UnitOfLength.MILES
)
else:
self.distance = distance
if self.config_entry.options[CONF_UNITS] == IMPERIAL_UNITS:
# Convert to miles.
self.distance = DistanceConverter.convert(
distance, UnitOfLength.KILOMETERS, UnitOfLength.MILES
)
else:
self.distance = distance

self.route = route.name
except WRCError as exp:
_LOGGER.warning("Error on retrieving data: %s", exp)
return
self.route = route.name
57 changes: 57 additions & 0 deletions homeassistant/components/waze_travel_time/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
get_travel_times:
fields:
origin:
required: true
example: "38.9"
selector:
text:
destination:
required: true
example: "-77.04833"
selector:
text:
region:
required: true
default: "us"
selector:
select:
translation_key: region
options:
- us
- na
- eu
- il
- au
units:
default: "metric"
selector:
select:
translation_key: units
options:
- metric
- imperial
vehicle_type:
default: "car"
selector:
select:
translation_key: vehicle_type
options:
- car
- taxi
- motorcycle
realtime:
required: false
selector:
boolean:
avoid_toll_roads:
required: false
selector:
boolean:
avoid_ferries:
required: false
selector:
boolean:
avoid_subscription_roads:
required: false
selector:
boolean:

0 comments on commit 3844e2d

Please sign in to comment.