Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
317a6cd
Add config flow for Waze Travel Time
raman325 Nov 20, 2020
c9421fb
update translations
raman325 Nov 20, 2020
0e177fd
setup entry is async
raman325 Nov 20, 2020
c077ba9
fix update logic during setup
raman325 Nov 20, 2020
d261298
support old config method in the interim
raman325 Nov 21, 2020
2104206
fix requirements
raman325 Nov 21, 2020
cfd14d4
fix requirements
raman325 Nov 21, 2020
a404af9
add abort string
raman325 Nov 22, 2020
8c267a7
changes based on @bdraco review
raman325 Nov 24, 2020
72b8f56
fix tests
raman325 Nov 24, 2020
e21c47f
add device identifier
raman325 Dec 5, 2020
949f588
Update homeassistant/components/waze_travel_time/__init__.py
raman325 Jan 26, 2021
9da8dfc
fix tests
raman325 Jan 26, 2021
383fa81
Merge branch 'dev' into waze_config_flow
raman325 Mar 28, 2021
1c9c7d7
Update homeassistant/components/waze_travel_time/sensor.py
raman325 Mar 29, 2021
7b5cf85
log warning for deprecation message
raman325 Mar 29, 2021
dc415b4
PR feedback
raman325 Mar 29, 2021
5b110ad
fix tests and bugs
raman325 Mar 29, 2021
b7b834f
re-add name to config schema to avoid breaking change
raman325 Mar 29, 2021
6c0e25c
handle if we get name from config in entry title
raman325 Mar 29, 2021
d2545ac
fix name logic
raman325 Mar 29, 2021
ef2a06c
always set up options with defaults
raman325 Mar 29, 2021
ab4d9b3
Update homeassistant/components/waze_travel_time/sensor.py
raman325 Mar 29, 2021
b6675c0
Update config_flow.py
raman325 Mar 29, 2021
add53b2
Update sensor.py
raman325 Mar 29, 2021
5778963
handle options updates by getting options on every update
raman325 Mar 29, 2021
4482be3
patch library instead of sensor
raman325 Mar 30, 2021
80a7b94
fixes and make sure first update writes the state
raman325 Mar 30, 2021
3ee638f
validate config entry data during config flow and entry setup
raman325 Mar 30, 2021
7cc8d62
fix input parameters
raman325 Mar 30, 2021
c39d7b0
fix tests
raman325 Mar 30, 2021
7ee56ba
invert if statement
raman325 Mar 31, 2021
c99f00d
remove unnecessary else
raman325 Mar 31, 2021
23b5291
exclude helpers from coverage
raman325 Apr 5, 2021
f6b2497
remove async_setup because it's no longer needed
raman325 Apr 5, 2021
421516c
fix patch statements
raman325 Apr 5, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,8 @@ omit =
homeassistant/components/waterfurnace/*
homeassistant/components/watson_iot/*
homeassistant/components/watson_tts/tts.py
homeassistant/components/waze_travel_time/__init__.py
homeassistant/components/waze_travel_time/helpers.py
homeassistant/components/waze_travel_time/sensor.py
homeassistant/components/webostv/*
homeassistant/components/whois/sensor.py
Expand Down
28 changes: 28 additions & 0 deletions homeassistant/components/waze_travel_time/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,29 @@
"""The waze_travel_time component."""
import asyncio

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

PLATFORMS = ["sensor"]


async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Load the saved entities."""
for platform in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)

return True


async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(config_entry, platform)
for platform in PLATFORMS
]
)
)
149 changes: 149 additions & 0 deletions homeassistant/components/waze_travel_time/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"""Config flow for Waze Travel Time integration."""
import logging

import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import CONF_NAME, CONF_REGION
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.util import slugify

from .const import (
CONF_AVOID_FERRIES,
CONF_AVOID_SUBSCRIPTION_ROADS,
CONF_AVOID_TOLL_ROADS,
CONF_DESTINATION,
CONF_EXCL_FILTER,
CONF_INCL_FILTER,
CONF_ORIGIN,
CONF_REALTIME,
CONF_UNITS,
CONF_VEHICLE_TYPE,
DEFAULT_NAME,
DOMAIN,
REGIONS,
UNITS,
VEHICLE_TYPES,
)
from .helpers import is_valid_config_entry

_LOGGER = logging.getLogger(__name__)


class WazeOptionsFlow(config_entries.OptionsFlow):
"""Handle an options flow for Waze Travel Time."""

def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
"""Initialize waze options flow."""
self.config_entry = config_entry

async def async_step_init(self, user_input=None):
"""Handle the initial step."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)

return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_INCL_FILTER,
default=self.config_entry.options.get(CONF_INCL_FILTER),
): cv.string,
vol.Optional(
CONF_EXCL_FILTER,
default=self.config_entry.options.get(CONF_EXCL_FILTER),
): cv.string,
vol.Optional(
CONF_REALTIME,
default=self.config_entry.options[CONF_REALTIME],
): cv.boolean,
vol.Optional(
CONF_VEHICLE_TYPE,
default=self.config_entry.options[CONF_VEHICLE_TYPE],
): vol.In(VEHICLE_TYPES),
vol.Optional(
CONF_UNITS,
default=self.config_entry.options[CONF_UNITS],
): vol.In(UNITS),
vol.Optional(
CONF_AVOID_TOLL_ROADS,
default=self.config_entry.options[CONF_AVOID_TOLL_ROADS],
): cv.boolean,
vol.Optional(
CONF_AVOID_SUBSCRIPTION_ROADS,
default=self.config_entry.options[
CONF_AVOID_SUBSCRIPTION_ROADS
],
): cv.boolean,
vol.Optional(
CONF_AVOID_FERRIES,
default=self.config_entry.options[CONF_AVOID_FERRIES],
): cv.boolean,
}
),
)


class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Waze Travel Time."""

VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL

@staticmethod
@callback
def async_get_options_flow(
config_entry: config_entries.ConfigEntry,
) -> WazeOptionsFlow:
"""Get the options flow for this handler."""
return WazeOptionsFlow(config_entry)

async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}
if user_input is not None:
if await self.hass.async_add_executor_job(
is_valid_config_entry,
self.hass,
_LOGGER,
user_input[CONF_ORIGIN],
user_input[CONF_DESTINATION],
user_input[CONF_REGION],
):
await self.async_set_unique_id(
slugify(
f"{DOMAIN}_{user_input[CONF_ORIGIN]}_{user_input[CONF_DESTINATION]}"
)
)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=(
user_input.get(
CONF_NAME,
(
f"{DEFAULT_NAME}: {user_input[CONF_ORIGIN]} -> "
f"{user_input[CONF_DESTINATION]}"
),
)
),
data=user_input,
)

# If we get here, it's because we couldn't connect
errors["base"] = "cannot_connect"

return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_ORIGIN): cv.string,
vol.Required(CONF_DESTINATION): cv.string,
vol.Required(CONF_REGION): vol.In(REGIONS),
}
),
errors=errors,
)

async_step_import = async_step_user
40 changes: 40 additions & 0 deletions homeassistant/components/waze_travel_time/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Constants for waze_travel_time."""
from homeassistant.const import CONF_UNIT_SYSTEM_IMPERIAL, CONF_UNIT_SYSTEM_METRIC

DOMAIN = "waze_travel_time"

ATTR_DESTINATION = "destination"
ATTR_DURATION = "duration"
ATTR_DISTANCE = "distance"
ATTR_ORIGIN = "origin"
ATTR_ROUTE = "route"

ATTRIBUTION = "Powered by Waze"

CONF_DESTINATION = "destination"
CONF_ORIGIN = "origin"
CONF_INCL_FILTER = "incl_filter"
CONF_EXCL_FILTER = "excl_filter"
CONF_REALTIME = "realtime"
CONF_UNITS = "units"
CONF_VEHICLE_TYPE = "vehicle_type"
CONF_AVOID_TOLL_ROADS = "avoid_toll_roads"
CONF_AVOID_SUBSCRIPTION_ROADS = "avoid_subscription_roads"
CONF_AVOID_FERRIES = "avoid_ferries"

DEFAULT_NAME = "Waze Travel Time"
DEFAULT_REALTIME = True
DEFAULT_VEHICLE_TYPE = "car"
DEFAULT_AVOID_TOLL_ROADS = False
DEFAULT_AVOID_SUBSCRIPTION_ROADS = False
DEFAULT_AVOID_FERRIES = False

ICON = "mdi:car"

UNITS = [CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL]

REGIONS = ["US", "NA", "EU", "IL", "AU"]
VEHICLE_TYPES = ["car", "taxi", "motorcycle"]

# Attempt to find entity_id without finding address with period.
ENTITY_ID_PATTERN = "(?<![a-zA-Z0-9 ])[a-z_]+[.][a-zA-Z0-9_]+"
72 changes: 72 additions & 0 deletions homeassistant/components/waze_travel_time/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""Helpers for Waze Travel Time integration."""
import re

from WazeRouteCalculator import WazeRouteCalculator, WRCError

from homeassistant.components.waze_travel_time.const import ENTITY_ID_PATTERN
from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE
from homeassistant.helpers import location


def is_valid_config_entry(hass, logger, origin, destination, region):
"""Return whether the config entry data is valid."""
origin = resolve_location(hass, logger, origin)
destination = resolve_location(hass, logger, destination)
try:
WazeRouteCalculator(origin, destination, region).calc_all_routes_info()
except WRCError:
return False
return True


def resolve_location(hass, logger, loc):
"""Resolve a location."""
if re.fullmatch(ENTITY_ID_PATTERN, loc):
return get_location_from_entity(hass, logger, loc)

return resolve_zone(hass, loc)


def get_location_from_entity(hass, logger, entity_id):
"""Get the location from the entity_id."""
state = hass.states.get(entity_id)

if state is None:
logger.error("Unable to find entity %s", entity_id)
return None

# Check if the entity has location attributes.
if location.has_location(state):
logger.debug("Getting %s location", entity_id)
return _get_location_from_attributes(state)

# Check if device is inside a zone.
zone_state = hass.states.get(f"zone.{state.state}")
if location.has_location(zone_state):
logger.debug(
"%s is in %s, getting zone location", entity_id, zone_state.entity_id
)
return _get_location_from_attributes(zone_state)

# If zone was not found in state then use the state as the location.
if entity_id.startswith("sensor."):
return state.state

# When everything fails just return nothing.
return None


def resolve_zone(hass, friendly_name):
"""Get a lat/long from a zones friendly_name."""
states = hass.states.all()
for state in states:
if state.domain == "zone" and state.name == friendly_name:
return _get_location_from_attributes(state)

return friendly_name


def _get_location_from_attributes(state):
"""Get the lat/long string from an states attributes."""
attr = state.attributes
return "{},{}".format(attr.get(ATTR_LATITUDE), attr.get(ATTR_LONGITUDE))
7 changes: 5 additions & 2 deletions homeassistant/components/waze_travel_time/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"domain": "waze_travel_time",
"name": "Waze Travel Time",
"documentation": "https://www.home-assistant.io/integrations/waze_travel_time",
"requirements": ["WazeRouteCalculator==0.12"],
"codeowners": []
"requirements": [
"WazeRouteCalculator==0.12"
],
"codeowners": [],
"config_flow": true
}
Loading