Add option traffic_mode in here_travel_time (#146676)

This commit is contained in:
Kevin Stillhammer 2025-07-25 12:14:36 +02:00 committed by GitHub
parent 7e9da052ca
commit 95d4dc678c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 166 additions and 11 deletions

View File

@ -2,11 +2,13 @@
from __future__ import annotations
import logging
from homeassistant.const import CONF_API_KEY, CONF_MODE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.start import async_at_started
from .const import TRAVEL_MODE_PUBLIC
from .const import CONF_TRAFFIC_MODE, TRAVEL_MODE_PUBLIC
from .coordinator import (
HereConfigEntry,
HERERoutingDataUpdateCoordinator,
@ -15,6 +17,8 @@ from .coordinator import (
PLATFORMS = [Platform.SENSOR]
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, config_entry: HereConfigEntry) -> bool:
"""Set up HERE Travel Time from a config entry."""
@ -43,3 +47,28 @@ async def async_unload_entry(
) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
async def async_migrate_entry(
hass: HomeAssistant, config_entry: HereConfigEntry
) -> bool:
"""Migrate an old config entry."""
if config_entry.version == 1 and config_entry.minor_version == 1:
_LOGGER.debug(
"Migrating from version %s.%s",
config_entry.version,
config_entry.minor_version,
)
options = dict(config_entry.options)
options[CONF_TRAFFIC_MODE] = True
hass.config_entries.async_update_entry(
config_entry, options=options, version=1, minor_version=2
)
_LOGGER.debug(
"Migration to version %s.%s successful",
config_entry.version,
config_entry.minor_version,
)
return True

View File

@ -33,6 +33,7 @@ from homeassistant.const import (
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.selector import (
BooleanSelector,
EntitySelector,
LocationSelector,
TimeSelector,
@ -50,6 +51,7 @@ from .const import (
CONF_ORIGIN_LATITUDE,
CONF_ORIGIN_LONGITUDE,
CONF_ROUTE_MODE,
CONF_TRAFFIC_MODE,
DEFAULT_NAME,
DOMAIN,
ROUTE_MODE_FASTEST,
@ -65,6 +67,7 @@ DEFAULT_OPTIONS = {
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_ARRIVAL_TIME: None,
CONF_DEPARTURE_TIME: None,
CONF_TRAFFIC_MODE: True,
}
@ -102,6 +105,7 @@ class HERETravelTimeConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for HERE Travel Time."""
VERSION = 1
MINOR_VERSION = 2
def __init__(self) -> None:
"""Init Config Flow."""
@ -307,7 +311,9 @@ class HERETravelTimeOptionsFlow(OptionsFlow):
"""Manage the HERE Travel Time options."""
if user_input is not None:
self._config = user_input
return await self.async_step_time_menu()
if self._config[CONF_TRAFFIC_MODE]:
return await self.async_step_time_menu()
return self.async_create_entry(title="", data=self._config)
schema = self.add_suggested_values_to_schema(
vol.Schema(
@ -318,12 +324,21 @@ class HERETravelTimeOptionsFlow(OptionsFlow):
CONF_ROUTE_MODE, DEFAULT_OPTIONS[CONF_ROUTE_MODE]
),
): vol.In(ROUTE_MODES),
vol.Optional(
CONF_TRAFFIC_MODE,
default=self.config_entry.options.get(
CONF_TRAFFIC_MODE, DEFAULT_OPTIONS[CONF_TRAFFIC_MODE]
),
): BooleanSelector(),
}
),
{
CONF_ROUTE_MODE: self.config_entry.options.get(
CONF_ROUTE_MODE, DEFAULT_OPTIONS[CONF_ROUTE_MODE]
),
CONF_TRAFFIC_MODE: self.config_entry.options.get(
CONF_TRAFFIC_MODE, DEFAULT_OPTIONS[CONF_TRAFFIC_MODE]
),
},
)

View File

@ -19,6 +19,7 @@ CONF_ARRIVAL = "arrival"
CONF_DEPARTURE = "departure"
CONF_ARRIVAL_TIME = "arrival_time"
CONF_DEPARTURE_TIME = "departure_time"
CONF_TRAFFIC_MODE = "traffic_mode"
DEFAULT_NAME = "HERE Travel Time"

View File

@ -13,6 +13,7 @@ from here_routing import (
Return,
RoutingMode,
Spans,
TrafficMode,
TransportMode,
)
import here_transit
@ -44,6 +45,7 @@ from .const import (
CONF_ORIGIN_LATITUDE,
CONF_ORIGIN_LONGITUDE,
CONF_ROUTE_MODE,
CONF_TRAFFIC_MODE,
DEFAULT_SCAN_INTERVAL,
DOMAIN,
ROUTE_MODE_FASTEST,
@ -87,7 +89,7 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator[HERETravelTimeData]
_LOGGER.debug(
(
"Requesting route for origin: %s, destination: %s, route_mode: %s,"
" mode: %s, arrival: %s, departure: %s"
" mode: %s, arrival: %s, departure: %s, traffic_mode: %s"
),
params.origin,
params.destination,
@ -95,6 +97,7 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator[HERETravelTimeData]
TransportMode(params.travel_mode),
params.arrival,
params.departure,
params.traffic_mode,
)
try:
@ -109,6 +112,7 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator[HERETravelTimeData]
routing_mode=params.route_mode,
arrival_time=params.arrival,
departure_time=params.departure,
traffic_mode=params.traffic_mode,
return_values=[Return.POLYINE, Return.SUMMARY],
spans=[Spans.NAMES],
)
@ -350,6 +354,11 @@ def prepare_parameters(
if config_entry.options[CONF_ROUTE_MODE] == ROUTE_MODE_FASTEST
else RoutingMode.SHORT
)
traffic_mode = (
TrafficMode.DISABLED
if config_entry.options[CONF_TRAFFIC_MODE] is False
else TrafficMode.DEFAULT
)
return HERETravelTimeAPIParams(
destination=destination,
@ -358,6 +367,7 @@ def prepare_parameters(
route_mode=route_mode,
arrival=arrival,
departure=departure,
traffic_mode=traffic_mode,
)

View File

@ -6,7 +6,7 @@ from dataclasses import dataclass
from datetime import datetime
from typing import TypedDict
from here_routing import RoutingMode
from here_routing import RoutingMode, TrafficMode
class HERETravelTimeData(TypedDict):
@ -32,3 +32,4 @@ class HERETravelTimeAPIParams:
route_mode: RoutingMode
arrival: datetime | None
departure: datetime | None
traffic_mode: TrafficMode

View File

@ -60,8 +60,11 @@
"step": {
"init": {
"data": {
"traffic_mode": "Traffic mode",
"traffic_mode": "Use traffic and time-aware routing",
"route_mode": "Route mode"
},
"data_description": {
"traffic_mode": "Needed for defining arrival/departure times"
}
},
"time_menu": {

View File

@ -6,7 +6,10 @@ from here_routing import HERERoutingError, HERERoutingUnauthorizedError
import pytest
from homeassistant import config_entries
from homeassistant.components.here_travel_time.config_flow import DEFAULT_OPTIONS
from homeassistant.components.here_travel_time.config_flow import (
DEFAULT_OPTIONS,
HERETravelTimeConfigFlow,
)
from homeassistant.components.here_travel_time.const import (
CONF_ARRIVAL_TIME,
CONF_DEPARTURE_TIME,
@ -17,6 +20,7 @@ from homeassistant.components.here_travel_time.const import (
CONF_ORIGIN_LATITUDE,
CONF_ORIGIN_LONGITUDE,
CONF_ROUTE_MODE,
CONF_TRAFFIC_MODE,
DOMAIN,
ROUTE_MODE_FASTEST,
TRAVEL_MODE_BICYCLE,
@ -86,6 +90,8 @@ async def option_init_result_fixture(
CONF_MODE: TRAVEL_MODE_PUBLIC,
CONF_NAME: "test",
},
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -249,6 +255,7 @@ async def test_step_destination_entity(
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_ARRIVAL_TIME: None,
CONF_DEPARTURE_TIME: None,
CONF_TRAFFIC_MODE: True,
}
@ -317,6 +324,8 @@ async def do_common_reconfiguration_steps(hass: HomeAssistant) -> None:
unique_id="0123456789",
data=DEFAULT_CONFIG,
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
@ -398,6 +407,8 @@ async def test_options_flow(hass: HomeAssistant) -> None:
domain=DOMAIN,
unique_id="0123456789",
data=DEFAULT_CONFIG,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
@ -414,10 +425,16 @@ async def test_options_flow(hass: HomeAssistant) -> None:
result["flow_id"],
user_input={
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_TRAFFIC_MODE: False,
},
)
assert result["type"] is FlowResultType.MENU
assert result["type"] is FlowResultType.CREATE_ENTRY
entry = hass.config_entries.async_entries(DOMAIN)[0]
assert entry.options == {
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_TRAFFIC_MODE: False,
}
@pytest.mark.usefixtures("valid_response")
@ -441,6 +458,7 @@ async def test_options_flow_arrival_time_step(
assert entry.options == {
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_ARRIVAL_TIME: "08:00:00",
CONF_TRAFFIC_MODE: True,
}
@ -465,6 +483,7 @@ async def test_options_flow_departure_time_step(
assert entry.options == {
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_DEPARTURE_TIME: "08:00:00",
CONF_TRAFFIC_MODE: True,
}
@ -481,4 +500,5 @@ async def test_options_flow_no_time_step(
entry = hass.config_entries.async_entries(DOMAIN)[0]
assert entry.options == {
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_TRAFFIC_MODE: True,
}

View File

@ -4,14 +4,19 @@ from datetime import datetime
import pytest
from homeassistant.components.here_travel_time.config_flow import DEFAULT_OPTIONS
from homeassistant.components.here_travel_time.config_flow import (
DEFAULT_OPTIONS,
HERETravelTimeConfigFlow,
)
from homeassistant.components.here_travel_time.const import (
CONF_ARRIVAL_TIME,
CONF_DEPARTURE_TIME,
CONF_ROUTE_MODE,
CONF_TRAFFIC_MODE,
DOMAIN,
ROUTE_MODE_FASTEST,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from .const import DEFAULT_CONFIG
@ -44,9 +49,34 @@ async def test_unload_entry(hass: HomeAssistant, options) -> None:
unique_id="0123456789",
data=DEFAULT_CONFIG,
options=options,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert await hass.config_entries.async_unload(entry.entry_id)
@pytest.mark.usefixtures("valid_response")
async def test_migrate_entry_v1_1_v1_2(
hass: HomeAssistant,
) -> None:
"""Test successful migration of entry data."""
mock_entry = MockConfigEntry(
domain=DOMAIN,
data=DEFAULT_CONFIG,
options=DEFAULT_OPTIONS,
version=1,
minor_version=1,
)
mock_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done()
updated_entry = hass.config_entries.async_get_entry(mock_entry.entry_id)
assert updated_entry.state is ConfigEntryState.LOADED
assert updated_entry.minor_version == 2
assert updated_entry.options[CONF_TRAFFIC_MODE] is True

View File

@ -11,6 +11,7 @@ from here_routing import (
Return,
RoutingMode,
Spans,
TrafficMode,
TransportMode,
)
from here_transit import (
@ -21,7 +22,10 @@ from here_transit import (
)
import pytest
from homeassistant.components.here_travel_time.config_flow import DEFAULT_OPTIONS
from homeassistant.components.here_travel_time.config_flow import (
DEFAULT_OPTIONS,
HERETravelTimeConfigFlow,
)
from homeassistant.components.here_travel_time.const import (
CONF_ARRIVAL_TIME,
CONF_DEPARTURE_TIME,
@ -32,6 +36,7 @@ from homeassistant.components.here_travel_time.const import (
CONF_ORIGIN_LATITUDE,
CONF_ORIGIN_LONGITUDE,
CONF_ROUTE_MODE,
CONF_TRAFFIC_MODE,
DEFAULT_SCAN_INTERVAL,
DOMAIN,
ICON_BICYCLE,
@ -85,29 +90,33 @@ from tests.common import (
@pytest.mark.parametrize(
("mode", "icon", "arrival_time", "departure_time"),
("mode", "icon", "traffic_mode", "arrival_time", "departure_time"),
[
(
TRAVEL_MODE_CAR,
ICON_CAR,
False,
None,
None,
),
(
TRAVEL_MODE_BICYCLE,
ICON_BICYCLE,
True,
None,
None,
),
(
TRAVEL_MODE_PEDESTRIAN,
ICON_PEDESTRIAN,
True,
None,
"08:00:00",
),
(
TRAVEL_MODE_TRUCK,
ICON_TRUCK,
True,
None,
"08:00:00",
),
@ -118,6 +127,7 @@ async def test_sensor(
hass: HomeAssistant,
mode,
icon,
traffic_mode,
arrival_time,
departure_time,
) -> None:
@ -137,9 +147,12 @@ async def test_sensor(
},
options={
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_TRAFFIC_MODE: traffic_mode,
CONF_ARRIVAL_TIME: arrival_time,
CONF_DEPARTURE_TIME: departure_time,
},
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -197,6 +210,8 @@ async def test_circular_ref(
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -228,7 +243,10 @@ async def test_public_transport(hass: HomeAssistant) -> None:
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_ARRIVAL_TIME: "08:00:00",
CONF_DEPARTURE_TIME: None,
CONF_TRAFFIC_MODE: True,
},
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -260,6 +278,8 @@ async def test_no_attribution_response(hass: HomeAssistant) -> None:
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -307,6 +327,8 @@ async def test_entity_ids(hass: HomeAssistant, valid_response: MagicMock) -> Non
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -324,6 +346,7 @@ async def test_entity_ids(hass: HomeAssistant, valid_response: MagicMock) -> Non
routing_mode=RoutingMode.FAST,
arrival_time=None,
departure_time=None,
traffic_mode=TrafficMode.DEFAULT,
return_values=[Return.POLYINE, Return.SUMMARY],
spans=[Spans.NAMES],
)
@ -346,6 +369,8 @@ async def test_destination_entity_not_found(
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -374,6 +399,8 @@ async def test_origin_entity_not_found(
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -406,6 +433,8 @@ async def test_invalid_destination_entity_state(
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -440,6 +469,8 @@ async def test_invalid_origin_entity_state(
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -476,6 +507,8 @@ async def test_route_not_found(
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -587,7 +620,12 @@ async def test_restore_state(hass: HomeAssistant) -> None:
# create and add entry
mock_entry = MockConfigEntry(
domain=DOMAIN, unique_id=DOMAIN, data=DEFAULT_CONFIG, options=DEFAULT_OPTIONS
domain=DOMAIN,
unique_id=DOMAIN,
data=DEFAULT_CONFIG,
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
mock_entry.add_to_hass(hass)
@ -656,6 +694,8 @@ async def test_transit_errors(
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -682,6 +722,8 @@ async def test_routing_rate_limit(
unique_id="0123456789",
data=DEFAULT_CONFIG,
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -739,6 +781,8 @@ async def test_transit_rate_limit(
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -791,6 +835,8 @@ async def test_multiple_sections(
CONF_NAME: "test",
},
options=DEFAULT_OPTIONS,
version=HERETravelTimeConfigFlow.VERSION,
minor_version=HERETravelTimeConfigFlow.MINOR_VERSION,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)