From 5c0fa35d4a6a8733d800884a6e8bd4f1273d0381 Mon Sep 17 00:00:00 2001
From: Kevin Eifinger <k.eifinger@googlemail.com>
Date: Mon, 23 Sep 2019 11:50:18 +0200
Subject: [PATCH] Add here_travel_time (#24603)

* Add here_travel_time

* Bump herepy version to 0.6.2

* Update requirements_all.txt

* Disable pylint and catch errors

* Add herepy to requirements_test_all

* Correctly place test req for herepy

* use homeassistant.const.LENGTH_METERS

* Implemented Requested Changes

* Better error message for cryptic error code

* add requested changes

* add_entities instead of async

* Add route attr and distance in km instead of m

* fix linting errors

* attribute duration in minutes instead of seconds

* Correct pattern for longitude

* dont split attribute but rather local var

* move strings to const and use travelTime

* Add tests

* Add route for pedestrian and public

* fix public transport route generation

* remove print statement

* Standalone pytest

* Use hass fixture and increase test cov
_resolve_zone is redundant

* Clean up redundant code

* Add type annotations

* Readd _resolve_zone and add a test for it

* Full test cov

* use caplog

* Add origin/destination attributes
According to https://github.com/home-assistant/home-assistant/pull/24956

* Add mode: bicycle

* black

* Add mode: publicTransportTimeTable

* Fix error for publicTransportTimeTable
Switch route_mode and travel_mode in api request.

* split up config options

* More type hints

* implement *_entity_id

* align attributes with google_travel_time

* route in lib
apply requested changes

* Update requirements_all.txt

* remove DATA_KEY

* Use ATTR_MODE

* add attribution

* Only add attribution if not none

* Add debug log for raw response

* Add _build_hass_attribution

* clearer var names in credentials check

* async _are_valid_client_credentials
---
 CODEOWNERS                                    |   1 +
 .../components/here_travel_time/__init__.py   |   1 +
 .../components/here_travel_time/manifest.json |  12 +
 .../components/here_travel_time/sensor.py     | 431 ++++++++
 requirements_all.txt                          |   3 +
 requirements_test_all.txt                     |   3 +
 script/gen_requirements_all.py                |   1 +
 tests/components/here_travel_time/__init__.py |   1 +
 .../here_travel_time/test_sensor.py           | 947 ++++++++++++++++++
 .../attribution_response.json                 | 276 +++++
 .../here_travel_time/bike_response.json       | 274 +++++
 .../car_enabled_response.json                 | 298 ++++++
 .../here_travel_time/car_response.json        | 299 ++++++
 .../car_shortest_response.json                | 231 +++++
 .../here_travel_time/pedestrian_response.json | 308 ++++++
 .../here_travel_time/public_response.json     | 294 ++++++
 .../public_time_table_response.json           | 308 ++++++
 .../routing_error_invalid_credentials.json    |  15 +
 .../routing_error_no_route_found.json         |  21 +
 .../here_travel_time/truck_response.json      | 187 ++++
 20 files changed, 3911 insertions(+)
 create mode 100755 homeassistant/components/here_travel_time/__init__.py
 create mode 100755 homeassistant/components/here_travel_time/manifest.json
 create mode 100755 homeassistant/components/here_travel_time/sensor.py
 create mode 100644 tests/components/here_travel_time/__init__.py
 create mode 100644 tests/components/here_travel_time/test_sensor.py
 create mode 100644 tests/fixtures/here_travel_time/attribution_response.json
 create mode 100644 tests/fixtures/here_travel_time/bike_response.json
 create mode 100644 tests/fixtures/here_travel_time/car_enabled_response.json
 create mode 100644 tests/fixtures/here_travel_time/car_response.json
 create mode 100644 tests/fixtures/here_travel_time/car_shortest_response.json
 create mode 100644 tests/fixtures/here_travel_time/pedestrian_response.json
 create mode 100644 tests/fixtures/here_travel_time/public_response.json
 create mode 100644 tests/fixtures/here_travel_time/public_time_table_response.json
 create mode 100644 tests/fixtures/here_travel_time/routing_error_invalid_credentials.json
 create mode 100644 tests/fixtures/here_travel_time/routing_error_no_route_found.json
 create mode 100644 tests/fixtures/here_travel_time/truck_response.json

diff --git a/CODEOWNERS b/CODEOWNERS
index ae072cd092c..7e05cdf0b39 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -115,6 +115,7 @@ homeassistant/components/gtfs/* @robbiet480
 homeassistant/components/harmony/* @ehendrix23
 homeassistant/components/hassio/* @home-assistant/hass-io
 homeassistant/components/heos/* @andrewsayre
+homeassistant/components/here_travel_time/* @eifinger
 homeassistant/components/hikvision/* @mezz64
 homeassistant/components/hikvisioncam/* @fbradyirl
 homeassistant/components/history/* @home-assistant/core
diff --git a/homeassistant/components/here_travel_time/__init__.py b/homeassistant/components/here_travel_time/__init__.py
new file mode 100755
index 00000000000..9a5c8ec32ac
--- /dev/null
+++ b/homeassistant/components/here_travel_time/__init__.py
@@ -0,0 +1 @@
+"""The here_travel_time component."""
diff --git a/homeassistant/components/here_travel_time/manifest.json b/homeassistant/components/here_travel_time/manifest.json
new file mode 100755
index 00000000000..e26e2e1d6ea
--- /dev/null
+++ b/homeassistant/components/here_travel_time/manifest.json
@@ -0,0 +1,12 @@
+{
+    "domain": "here_travel_time",
+    "name": "HERE travel time",
+    "documentation": "https://www.home-assistant.io/components/here_travel_time",
+    "requirements": [
+      "herepy==0.6.3.1"
+    ],
+    "dependencies": [],
+    "codeowners": [
+      "@eifinger"
+    ]
+  }
diff --git a/homeassistant/components/here_travel_time/sensor.py b/homeassistant/components/here_travel_time/sensor.py
new file mode 100755
index 00000000000..ba4908fe85c
--- /dev/null
+++ b/homeassistant/components/here_travel_time/sensor.py
@@ -0,0 +1,431 @@
+"""Support for HERE travel time sensors."""
+from datetime import timedelta
+import logging
+from typing import Callable, Dict, Optional, Union
+
+import herepy
+import voluptuous as vol
+
+from homeassistant.components.sensor import PLATFORM_SCHEMA
+from homeassistant.const import (
+    ATTR_ATTRIBUTION,
+    ATTR_LATITUDE,
+    ATTR_LONGITUDE,
+    CONF_MODE,
+    CONF_NAME,
+    CONF_UNIT_SYSTEM,
+    CONF_UNIT_SYSTEM_IMPERIAL,
+    CONF_UNIT_SYSTEM_METRIC,
+)
+from homeassistant.core import HomeAssistant, State
+from homeassistant.helpers import location
+import homeassistant.helpers.config_validation as cv
+from homeassistant.helpers.entity import Entity
+
+_LOGGER = logging.getLogger(__name__)
+
+CONF_DESTINATION_LATITUDE = "destination_latitude"
+CONF_DESTINATION_LONGITUDE = "destination_longitude"
+CONF_DESTINATION_ENTITY_ID = "destination_entity_id"
+CONF_ORIGIN_LATITUDE = "origin_latitude"
+CONF_ORIGIN_LONGITUDE = "origin_longitude"
+CONF_ORIGIN_ENTITY_ID = "origin_entity_id"
+CONF_APP_ID = "app_id"
+CONF_APP_CODE = "app_code"
+CONF_TRAFFIC_MODE = "traffic_mode"
+CONF_ROUTE_MODE = "route_mode"
+
+DEFAULT_NAME = "HERE Travel Time"
+
+TRAVEL_MODE_BICYCLE = "bicycle"
+TRAVEL_MODE_CAR = "car"
+TRAVEL_MODE_PEDESTRIAN = "pedestrian"
+TRAVEL_MODE_PUBLIC = "publicTransport"
+TRAVEL_MODE_PUBLIC_TIME_TABLE = "publicTransportTimeTable"
+TRAVEL_MODE_TRUCK = "truck"
+TRAVEL_MODE = [
+    TRAVEL_MODE_BICYCLE,
+    TRAVEL_MODE_CAR,
+    TRAVEL_MODE_PEDESTRIAN,
+    TRAVEL_MODE_PUBLIC,
+    TRAVEL_MODE_PUBLIC_TIME_TABLE,
+    TRAVEL_MODE_TRUCK,
+]
+
+TRAVEL_MODES_PUBLIC = [TRAVEL_MODE_PUBLIC, TRAVEL_MODE_PUBLIC_TIME_TABLE]
+TRAVEL_MODES_VEHICLE = [TRAVEL_MODE_CAR, TRAVEL_MODE_TRUCK]
+TRAVEL_MODES_NON_VEHICLE = [TRAVEL_MODE_BICYCLE, TRAVEL_MODE_PEDESTRIAN]
+
+TRAFFIC_MODE_ENABLED = "traffic_enabled"
+TRAFFIC_MODE_DISABLED = "traffic_disabled"
+
+ROUTE_MODE_FASTEST = "fastest"
+ROUTE_MODE_SHORTEST = "shortest"
+ROUTE_MODE = [ROUTE_MODE_FASTEST, ROUTE_MODE_SHORTEST]
+
+ICON_BICYCLE = "mdi:bike"
+ICON_CAR = "mdi:car"
+ICON_PEDESTRIAN = "mdi:walk"
+ICON_PUBLIC = "mdi:bus"
+ICON_TRUCK = "mdi:truck"
+
+UNITS = [CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL]
+
+ATTR_DURATION = "duration"
+ATTR_DISTANCE = "distance"
+ATTR_ROUTE = "route"
+ATTR_ORIGIN = "origin"
+ATTR_DESTINATION = "destination"
+
+ATTR_MODE = "mode"
+ATTR_UNIT_SYSTEM = CONF_UNIT_SYSTEM
+ATTR_TRAFFIC_MODE = CONF_TRAFFIC_MODE
+
+ATTR_DURATION_IN_TRAFFIC = "duration_in_traffic"
+ATTR_ORIGIN_NAME = "origin_name"
+ATTR_DESTINATION_NAME = "destination_name"
+
+UNIT_OF_MEASUREMENT = "min"
+
+SCAN_INTERVAL = timedelta(minutes=5)
+
+TRACKABLE_DOMAINS = ["device_tracker", "sensor", "zone", "person"]
+
+NO_ROUTE_ERROR_MESSAGE = "HERE could not find a route based on the input"
+
+COORDINATE_SCHEMA = vol.Schema(
+    {
+        vol.Inclusive(CONF_DESTINATION_LATITUDE, "coordinates"): cv.latitude,
+        vol.Inclusive(CONF_DESTINATION_LONGITUDE, "coordinates"): cv.longitude,
+    }
+)
+
+PLATFORM_SCHEMA = vol.All(
+    cv.has_at_least_one_key(CONF_DESTINATION_LATITUDE, CONF_DESTINATION_ENTITY_ID),
+    cv.has_at_least_one_key(CONF_ORIGIN_LATITUDE, CONF_ORIGIN_ENTITY_ID),
+    PLATFORM_SCHEMA.extend(
+        {
+            vol.Required(CONF_APP_ID): cv.string,
+            vol.Required(CONF_APP_CODE): cv.string,
+            vol.Inclusive(
+                CONF_DESTINATION_LATITUDE, "destination_coordinates"
+            ): cv.latitude,
+            vol.Inclusive(
+                CONF_DESTINATION_LONGITUDE, "destination_coordinates"
+            ): cv.longitude,
+            vol.Exclusive(CONF_DESTINATION_LATITUDE, "destination"): cv.latitude,
+            vol.Exclusive(CONF_DESTINATION_ENTITY_ID, "destination"): cv.entity_id,
+            vol.Inclusive(CONF_ORIGIN_LATITUDE, "origin_coordinates"): cv.latitude,
+            vol.Inclusive(CONF_ORIGIN_LONGITUDE, "origin_coordinates"): cv.longitude,
+            vol.Exclusive(CONF_ORIGIN_LATITUDE, "origin"): cv.latitude,
+            vol.Exclusive(CONF_ORIGIN_ENTITY_ID, "origin"): cv.entity_id,
+            vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
+            vol.Optional(CONF_MODE, default=TRAVEL_MODE_CAR): vol.In(TRAVEL_MODE),
+            vol.Optional(CONF_ROUTE_MODE, default=ROUTE_MODE_FASTEST): vol.In(
+                ROUTE_MODE
+            ),
+            vol.Optional(CONF_TRAFFIC_MODE, default=False): cv.boolean,
+            vol.Optional(CONF_UNIT_SYSTEM): vol.In(UNITS),
+        }
+    ),
+)
+
+
+async def async_setup_platform(
+    hass: HomeAssistant,
+    config: Dict[str, Union[str, bool]],
+    async_add_entities: Callable,
+    discovery_info: None = None,
+) -> None:
+    """Set up the HERE travel time platform."""
+
+    app_id = config[CONF_APP_ID]
+    app_code = config[CONF_APP_CODE]
+    here_client = herepy.RoutingApi(app_id, app_code)
+
+    if not await hass.async_add_executor_job(
+        _are_valid_client_credentials, here_client
+    ):
+        _LOGGER.error(
+            "Invalid credentials. This error is returned if the specified token was invalid or no contract could be found for this token."
+        )
+        return
+
+    if config.get(CONF_ORIGIN_LATITUDE) is not None:
+        origin = f"{config[CONF_ORIGIN_LATITUDE]},{config[CONF_ORIGIN_LONGITUDE]}"
+    else:
+        origin = config[CONF_ORIGIN_ENTITY_ID]
+
+    if config.get(CONF_DESTINATION_LATITUDE) is not None:
+        destination = (
+            f"{config[CONF_DESTINATION_LATITUDE]},{config[CONF_DESTINATION_LONGITUDE]}"
+        )
+    else:
+        destination = config[CONF_DESTINATION_ENTITY_ID]
+
+    travel_mode = config[CONF_MODE]
+    traffic_mode = config[CONF_TRAFFIC_MODE]
+    route_mode = config[CONF_ROUTE_MODE]
+    name = config[CONF_NAME]
+    units = config.get(CONF_UNIT_SYSTEM, hass.config.units.name)
+
+    here_data = HERETravelTimeData(
+        here_client, travel_mode, traffic_mode, route_mode, units
+    )
+
+    sensor = HERETravelTimeSensor(name, origin, destination, here_data)
+
+    async_add_entities([sensor], True)
+
+
+def _are_valid_client_credentials(here_client: herepy.RoutingApi) -> bool:
+    """Check if the provided credentials are correct using defaults."""
+    known_working_origin = [38.9, -77.04833]
+    known_working_destination = [39.0, -77.1]
+    try:
+        here_client.car_route(
+            known_working_origin,
+            known_working_destination,
+            [
+                herepy.RouteMode[ROUTE_MODE_FASTEST],
+                herepy.RouteMode[TRAVEL_MODE_CAR],
+                herepy.RouteMode[TRAFFIC_MODE_DISABLED],
+            ],
+        )
+    except herepy.InvalidCredentialsError:
+        return False
+    return True
+
+
+class HERETravelTimeSensor(Entity):
+    """Representation of a HERE travel time sensor."""
+
+    def __init__(
+        self, name: str, origin: str, destination: str, here_data: "HERETravelTimeData"
+    ) -> None:
+        """Initialize the sensor."""
+        self._name = name
+        self._here_data = here_data
+        self._unit_of_measurement = UNIT_OF_MEASUREMENT
+        self._origin_entity_id = None
+        self._destination_entity_id = None
+        self._attrs = {
+            ATTR_UNIT_SYSTEM: self._here_data.units,
+            ATTR_MODE: self._here_data.travel_mode,
+            ATTR_TRAFFIC_MODE: self._here_data.traffic_mode,
+        }
+
+        # Check if location is a trackable entity
+        if origin.split(".", 1)[0] in TRACKABLE_DOMAINS:
+            self._origin_entity_id = origin
+        else:
+            self._here_data.origin = origin
+
+        if destination.split(".", 1)[0] in TRACKABLE_DOMAINS:
+            self._destination_entity_id = destination
+        else:
+            self._here_data.destination = destination
+
+    @property
+    def state(self) -> Optional[str]:
+        """Return the state of the sensor."""
+        if self._here_data.traffic_mode:
+            if self._here_data.traffic_time is not None:
+                return str(round(self._here_data.traffic_time / 60))
+        if self._here_data.base_time is not None:
+            return str(round(self._here_data.base_time / 60))
+
+        return None
+
+    @property
+    def name(self) -> str:
+        """Get the name of the sensor."""
+        return self._name
+
+    @property
+    def device_state_attributes(
+        self
+    ) -> Optional[Dict[str, Union[None, float, str, bool]]]:
+        """Return the state attributes."""
+        if self._here_data.base_time is None:
+            return None
+
+        res = self._attrs
+        if self._here_data.attribution is not None:
+            res[ATTR_ATTRIBUTION] = self._here_data.attribution
+        res[ATTR_DURATION] = self._here_data.base_time / 60
+        res[ATTR_DISTANCE] = self._here_data.distance
+        res[ATTR_ROUTE] = self._here_data.route
+        res[ATTR_DURATION_IN_TRAFFIC] = self._here_data.traffic_time / 60
+        res[ATTR_ORIGIN] = self._here_data.origin
+        res[ATTR_DESTINATION] = self._here_data.destination
+        res[ATTR_ORIGIN_NAME] = self._here_data.origin_name
+        res[ATTR_DESTINATION_NAME] = self._here_data.destination_name
+        return res
+
+    @property
+    def unit_of_measurement(self) -> str:
+        """Return the unit this state is expressed in."""
+        return self._unit_of_measurement
+
+    @property
+    def icon(self) -> str:
+        """Icon to use in the frontend depending on travel_mode."""
+        if self._here_data.travel_mode == TRAVEL_MODE_BICYCLE:
+            return ICON_BICYCLE
+        if self._here_data.travel_mode == TRAVEL_MODE_PEDESTRIAN:
+            return ICON_PEDESTRIAN
+        if self._here_data.travel_mode in TRAVEL_MODES_PUBLIC:
+            return ICON_PUBLIC
+        if self._here_data.travel_mode == TRAVEL_MODE_TRUCK:
+            return ICON_TRUCK
+        return ICON_CAR
+
+    async def async_update(self) -> None:
+        """Update Sensor Information."""
+        # Convert device_trackers to HERE friendly location
+        if self._origin_entity_id is not None:
+            self._here_data.origin = await self._get_location_from_entity(
+                self._origin_entity_id
+            )
+
+        if self._destination_entity_id is not None:
+            self._here_data.destination = await self._get_location_from_entity(
+                self._destination_entity_id
+            )
+
+        await self.hass.async_add_executor_job(self._here_data.update)
+
+    async def _get_location_from_entity(self, entity_id: str) -> Optional[str]:
+        """Get the location from the entity state or attributes."""
+        entity = self.hass.states.get(entity_id)
+
+        if entity is None:
+            _LOGGER.error("Unable to find entity %s", entity_id)
+            return None
+
+        # Check if the entity has location attributes
+        if location.has_location(entity):
+            return self._get_location_from_attributes(entity)
+
+        # Check if device is in a zone
+        zone_entity = self.hass.states.get("zone.{}".format(entity.state))
+        if location.has_location(zone_entity):
+            _LOGGER.debug(
+                "%s is in %s, getting zone location", entity_id, zone_entity.entity_id
+            )
+            return self._get_location_from_attributes(zone_entity)
+
+        # If zone was not found in state then use the state as the location
+        if entity_id.startswith("sensor."):
+            return entity.state
+
+    @staticmethod
+    def _get_location_from_attributes(entity: State) -> str:
+        """Get the lat/long string from an entities attributes."""
+        attr = entity.attributes
+        return "{},{}".format(attr.get(ATTR_LATITUDE), attr.get(ATTR_LONGITUDE))
+
+
+class HERETravelTimeData:
+    """HERETravelTime data object."""
+
+    def __init__(
+        self,
+        here_client: herepy.RoutingApi,
+        travel_mode: str,
+        traffic_mode: bool,
+        route_mode: str,
+        units: str,
+    ) -> None:
+        """Initialize herepy."""
+        self.origin = None
+        self.destination = None
+        self.travel_mode = travel_mode
+        self.traffic_mode = traffic_mode
+        self.route_mode = route_mode
+        self.attribution = None
+        self.traffic_time = None
+        self.distance = None
+        self.route = None
+        self.base_time = None
+        self.origin_name = None
+        self.destination_name = None
+        self.units = units
+        self._client = here_client
+
+    def update(self) -> None:
+        """Get the latest data from HERE."""
+        if self.traffic_mode:
+            traffic_mode = TRAFFIC_MODE_ENABLED
+        else:
+            traffic_mode = TRAFFIC_MODE_DISABLED
+
+        if self.destination is not None and self.origin is not None:
+            # Convert location to HERE friendly location
+            destination = self.destination.split(",")
+            origin = self.origin.split(",")
+
+            _LOGGER.debug(
+                "Requesting route for origin: %s, destination: %s, route_mode: %s, mode: %s, traffic_mode: %s",
+                origin,
+                destination,
+                herepy.RouteMode[self.route_mode],
+                herepy.RouteMode[self.travel_mode],
+                herepy.RouteMode[traffic_mode],
+            )
+            try:
+                response = self._client.car_route(
+                    origin,
+                    destination,
+                    [
+                        herepy.RouteMode[self.route_mode],
+                        herepy.RouteMode[self.travel_mode],
+                        herepy.RouteMode[traffic_mode],
+                    ],
+                )
+            except herepy.NoRouteFoundError:
+                # Better error message for cryptic no route error codes
+                _LOGGER.error(NO_ROUTE_ERROR_MESSAGE)
+                return
+
+            _LOGGER.debug("Raw response is: %s", response.response)
+
+            # pylint: disable=no-member
+            source_attribution = response.response.get("sourceAttribution")
+            if source_attribution is not None:
+                self.attribution = self._build_hass_attribution(source_attribution)
+            # pylint: disable=no-member
+            route = response.response["route"]
+            summary = route[0]["summary"]
+            waypoint = route[0]["waypoint"]
+            self.base_time = summary["baseTime"]
+            if self.travel_mode in TRAVEL_MODES_VEHICLE:
+                self.traffic_time = summary["trafficTime"]
+            else:
+                self.traffic_time = self.base_time
+            distance = summary["distance"]
+            if self.units == CONF_UNIT_SYSTEM_IMPERIAL:
+                # Convert to miles.
+                self.distance = distance / 1609.344
+            else:
+                # Convert to kilometers
+                self.distance = distance / 1000
+            # pylint: disable=no-member
+            self.route = response.route_short
+            self.origin_name = waypoint[0]["mappedRoadName"]
+            self.destination_name = waypoint[1]["mappedRoadName"]
+
+    @staticmethod
+    def _build_hass_attribution(source_attribution: Dict) -> Optional[str]:
+        """Build a hass frontend ready string out of the sourceAttribution."""
+        suppliers = source_attribution.get("supplier")
+        if suppliers is not None:
+            supplier_titles = []
+            for supplier in suppliers:
+                title = supplier.get("title")
+                if title is not None:
+                    supplier_titles.append(title)
+            joined_supplier_titles = ",".join(supplier_titles)
+            attribution = f"With the support of {joined_supplier_titles}. All information is provided without warranty of any kind."
+            return attribution
diff --git a/requirements_all.txt b/requirements_all.txt
index a3c1548bc1a..bf217688d26 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -619,6 +619,9 @@ hdate==0.9.0
 # homeassistant.components.heatmiser
 heatmiserV3==0.9.1
 
+# homeassistant.components.here_travel_time
+herepy==0.6.3.1
+
 # homeassistant.components.hikvisioncam
 hikvision==0.4
 
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 6b4d7fbc089..9e846c9c416 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -171,6 +171,9 @@ hbmqtt==0.9.5
 # homeassistant.components.jewish_calendar
 hdate==0.9.0
 
+# homeassistant.components.here_travel_time
+herepy==0.6.3.1
+
 # homeassistant.components.pi_hole
 hole==0.5.0
 
diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py
index 384d50bccef..d74a57d678d 100755
--- a/script/gen_requirements_all.py
+++ b/script/gen_requirements_all.py
@@ -87,6 +87,7 @@ TEST_REQUIREMENTS = (
     "haversine",
     "hbmqtt",
     "hdate",
+    "herepy",
     "hole",
     "holidays",
     "home-assistant-frontend",
diff --git a/tests/components/here_travel_time/__init__.py b/tests/components/here_travel_time/__init__.py
new file mode 100644
index 00000000000..ac0ec709654
--- /dev/null
+++ b/tests/components/here_travel_time/__init__.py
@@ -0,0 +1 @@
+"""Tests for here_travel_time component."""
diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py
new file mode 100644
index 00000000000..783209690a3
--- /dev/null
+++ b/tests/components/here_travel_time/test_sensor.py
@@ -0,0 +1,947 @@
+"""The test for the here_travel_time sensor platform."""
+import logging
+from unittest.mock import patch
+import urllib
+
+import herepy
+import pytest
+
+from homeassistant.components.here_travel_time.sensor import (
+    ATTR_ATTRIBUTION,
+    ATTR_DESTINATION,
+    ATTR_DESTINATION_NAME,
+    ATTR_DISTANCE,
+    ATTR_DURATION,
+    ATTR_DURATION_IN_TRAFFIC,
+    ATTR_ORIGIN,
+    ATTR_ORIGIN_NAME,
+    ATTR_ROUTE,
+    CONF_MODE,
+    CONF_TRAFFIC_MODE,
+    CONF_UNIT_SYSTEM,
+    ICON_BICYCLE,
+    ICON_CAR,
+    ICON_PEDESTRIAN,
+    ICON_PUBLIC,
+    ICON_TRUCK,
+    NO_ROUTE_ERROR_MESSAGE,
+    ROUTE_MODE_FASTEST,
+    ROUTE_MODE_SHORTEST,
+    SCAN_INTERVAL,
+    TRAFFIC_MODE_DISABLED,
+    TRAFFIC_MODE_ENABLED,
+    TRAVEL_MODE_BICYCLE,
+    TRAVEL_MODE_CAR,
+    TRAVEL_MODE_PEDESTRIAN,
+    TRAVEL_MODE_PUBLIC,
+    TRAVEL_MODE_PUBLIC_TIME_TABLE,
+    TRAVEL_MODE_TRUCK,
+    UNIT_OF_MEASUREMENT,
+)
+from homeassistant.const import ATTR_ICON
+from homeassistant.setup import async_setup_component
+import homeassistant.util.dt as dt_util
+
+from tests.common import async_fire_time_changed, load_fixture
+
+DOMAIN = "sensor"
+
+PLATFORM = "here_travel_time"
+
+APP_ID = "test"
+APP_CODE = "test"
+
+TRUCK_ORIGIN_LATITUDE = "41.9798"
+TRUCK_ORIGIN_LONGITUDE = "-87.8801"
+TRUCK_DESTINATION_LATITUDE = "41.9043"
+TRUCK_DESTINATION_LONGITUDE = "-87.9216"
+
+BIKE_ORIGIN_LATITUDE = "41.9798"
+BIKE_ORIGIN_LONGITUDE = "-87.8801"
+BIKE_DESTINATION_LATITUDE = "41.9043"
+BIKE_DESTINATION_LONGITUDE = "-87.9216"
+
+CAR_ORIGIN_LATITUDE = "38.9"
+CAR_ORIGIN_LONGITUDE = "-77.04833"
+CAR_DESTINATION_LATITUDE = "39.0"
+CAR_DESTINATION_LONGITUDE = "-77.1"
+
+
+def _build_mock_url(origin, destination, modes, app_id, app_code, departure):
+    """Construct a url for HERE."""
+    base_url = "https://route.cit.api.here.com/routing/7.2/calculateroute.json?"
+    parameters = {
+        "waypoint0": origin,
+        "waypoint1": destination,
+        "mode": ";".join(str(herepy.RouteMode[mode]) for mode in modes),
+        "app_id": app_id,
+        "app_code": app_code,
+        "departure": departure,
+    }
+    url = base_url + urllib.parse.urlencode(parameters)
+    return url
+
+
+def _assert_truck_sensor(sensor):
+    """Assert that states and attributes are correct for truck_response."""
+    assert sensor.state == "14"
+    assert sensor.attributes.get("unit_of_measurement") == UNIT_OF_MEASUREMENT
+
+    assert sensor.attributes.get(ATTR_ATTRIBUTION) is None
+    assert sensor.attributes.get(ATTR_DURATION) == 13.533333333333333
+    assert sensor.attributes.get(ATTR_DISTANCE) == 13.049
+    assert sensor.attributes.get(ATTR_ROUTE) == (
+        "I-190; I-294 S - Tri-State Tollway; I-290 W - Eisenhower Expy W; "
+        "IL-64 W - E North Ave; I-290 E - Eisenhower Expy E; I-290"
+    )
+    assert sensor.attributes.get(CONF_UNIT_SYSTEM) == "metric"
+    assert sensor.attributes.get(ATTR_DURATION_IN_TRAFFIC) == 13.533333333333333
+    assert sensor.attributes.get(ATTR_ORIGIN) == ",".join(
+        [TRUCK_ORIGIN_LATITUDE, TRUCK_ORIGIN_LONGITUDE]
+    )
+    assert sensor.attributes.get(ATTR_DESTINATION) == ",".join(
+        [TRUCK_DESTINATION_LATITUDE, TRUCK_DESTINATION_LONGITUDE]
+    )
+    assert sensor.attributes.get(ATTR_ORIGIN_NAME) == ""
+    assert sensor.attributes.get(ATTR_DESTINATION_NAME) == "Eisenhower Expy E"
+    assert sensor.attributes.get(CONF_MODE) == TRAVEL_MODE_TRUCK
+    assert sensor.attributes.get(CONF_TRAFFIC_MODE) is False
+
+    assert sensor.attributes.get(ATTR_ICON) == ICON_TRUCK
+
+
+@pytest.fixture
+def requests_mock_credentials_check(requests_mock):
+    """Add the url used in the api validation to all requests mock."""
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_CAR, TRAFFIC_MODE_DISABLED]
+    response_url = _build_mock_url(
+        ",".join([CAR_ORIGIN_LATITUDE, CAR_ORIGIN_LONGITUDE]),
+        ",".join([CAR_DESTINATION_LATITUDE, CAR_DESTINATION_LONGITUDE]),
+        modes,
+        APP_ID,
+        APP_CODE,
+        "now",
+    )
+    requests_mock.get(
+        response_url, text=load_fixture("here_travel_time/car_response.json")
+    )
+    return requests_mock
+
+
+@pytest.fixture
+def requests_mock_truck_response(requests_mock_credentials_check):
+    """Return a requests_mock for truck respones."""
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_TRUCK, TRAFFIC_MODE_DISABLED]
+    response_url = _build_mock_url(
+        ",".join([TRUCK_ORIGIN_LATITUDE, TRUCK_ORIGIN_LONGITUDE]),
+        ",".join([TRUCK_DESTINATION_LATITUDE, TRUCK_DESTINATION_LONGITUDE]),
+        modes,
+        APP_ID,
+        APP_CODE,
+        "now",
+    )
+    requests_mock_credentials_check.get(
+        response_url, text=load_fixture("here_travel_time/truck_response.json")
+    )
+
+
+@pytest.fixture
+def requests_mock_car_disabled_response(requests_mock_credentials_check):
+    """Return a requests_mock for truck respones."""
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_CAR, TRAFFIC_MODE_DISABLED]
+    response_url = _build_mock_url(
+        ",".join([CAR_ORIGIN_LATITUDE, CAR_ORIGIN_LONGITUDE]),
+        ",".join([CAR_DESTINATION_LATITUDE, CAR_DESTINATION_LONGITUDE]),
+        modes,
+        APP_ID,
+        APP_CODE,
+        "now",
+    )
+    requests_mock_credentials_check.get(
+        response_url, text=load_fixture("here_travel_time/car_response.json")
+    )
+
+
+async def test_car(hass, requests_mock_car_disabled_response):
+    """Test that car works."""
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": CAR_ORIGIN_LATITUDE,
+            "origin_longitude": CAR_ORIGIN_LONGITUDE,
+            "destination_latitude": CAR_DESTINATION_LATITUDE,
+            "destination_longitude": CAR_DESTINATION_LONGITUDE,
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+        }
+    }
+
+    assert await async_setup_component(hass, DOMAIN, config)
+
+    sensor = hass.states.get("sensor.test")
+    assert sensor.state == "30"
+    assert sensor.attributes.get("unit_of_measurement") == UNIT_OF_MEASUREMENT
+
+    assert sensor.attributes.get(ATTR_ATTRIBUTION) is None
+    assert sensor.attributes.get(ATTR_DURATION) == 30.05
+    assert sensor.attributes.get(ATTR_DISTANCE) == 23.903
+    assert sensor.attributes.get(ATTR_ROUTE) == (
+        "US-29 - K St NW; US-29 - Whitehurst Fwy; "
+        "I-495 N - Capital Beltway; MD-187 S - Old Georgetown Rd"
+    )
+    assert sensor.attributes.get(CONF_UNIT_SYSTEM) == "metric"
+    assert sensor.attributes.get(ATTR_DURATION_IN_TRAFFIC) == 31.016666666666666
+    assert sensor.attributes.get(ATTR_ORIGIN) == ",".join(
+        [CAR_ORIGIN_LATITUDE, CAR_ORIGIN_LONGITUDE]
+    )
+    assert sensor.attributes.get(ATTR_DESTINATION) == ",".join(
+        [CAR_DESTINATION_LATITUDE, CAR_DESTINATION_LONGITUDE]
+    )
+    assert sensor.attributes.get(ATTR_ORIGIN_NAME) == "22nd St NW"
+    assert sensor.attributes.get(ATTR_DESTINATION_NAME) == "Service Rd S"
+    assert sensor.attributes.get(CONF_MODE) == TRAVEL_MODE_CAR
+    assert sensor.attributes.get(CONF_TRAFFIC_MODE) is False
+
+    assert sensor.attributes.get(ATTR_ICON) == ICON_CAR
+
+    # Test traffic mode disabled
+    assert sensor.attributes.get(ATTR_DURATION) != sensor.attributes.get(
+        ATTR_DURATION_IN_TRAFFIC
+    )
+
+
+async def test_traffic_mode_enabled(hass, requests_mock_credentials_check):
+    """Test that traffic mode enabled works."""
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_CAR, TRAFFIC_MODE_ENABLED]
+    response_url = _build_mock_url(
+        ",".join([CAR_ORIGIN_LATITUDE, CAR_ORIGIN_LONGITUDE]),
+        ",".join([CAR_DESTINATION_LATITUDE, CAR_DESTINATION_LONGITUDE]),
+        modes,
+        APP_ID,
+        APP_CODE,
+        "now",
+    )
+    requests_mock_credentials_check.get(
+        response_url, text=load_fixture("here_travel_time/car_enabled_response.json")
+    )
+
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": CAR_ORIGIN_LATITUDE,
+            "origin_longitude": CAR_ORIGIN_LONGITUDE,
+            "destination_latitude": CAR_DESTINATION_LATITUDE,
+            "destination_longitude": CAR_DESTINATION_LONGITUDE,
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "traffic_mode": True,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+
+    sensor = hass.states.get("sensor.test")
+
+    # Test traffic mode enabled
+    assert sensor.attributes.get(ATTR_DURATION) != sensor.attributes.get(
+        ATTR_DURATION_IN_TRAFFIC
+    )
+
+
+async def test_imperial(hass, requests_mock_car_disabled_response):
+    """Test that imperial units work."""
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": CAR_ORIGIN_LATITUDE,
+            "origin_longitude": CAR_ORIGIN_LONGITUDE,
+            "destination_latitude": CAR_DESTINATION_LATITUDE,
+            "destination_longitude": CAR_DESTINATION_LONGITUDE,
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "unit_system": "imperial",
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+
+    sensor = hass.states.get("sensor.test")
+    assert sensor.attributes.get(ATTR_DISTANCE) == 14.852635608048994
+
+
+async def test_route_mode_shortest(hass, requests_mock_credentials_check):
+    """Test that route mode shortest works."""
+    origin = "38.902981,-77.048338"
+    destination = "39.042158,-77.119116"
+    modes = [ROUTE_MODE_SHORTEST, TRAVEL_MODE_CAR, TRAFFIC_MODE_DISABLED]
+    response_url = _build_mock_url(origin, destination, modes, APP_ID, APP_CODE, "now")
+    requests_mock_credentials_check.get(
+        response_url, text=load_fixture("here_travel_time/car_shortest_response.json")
+    )
+
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": origin.split(",")[0],
+            "origin_longitude": origin.split(",")[1],
+            "destination_latitude": destination.split(",")[0],
+            "destination_longitude": destination.split(",")[1],
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "route_mode": ROUTE_MODE_SHORTEST,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+
+    sensor = hass.states.get("sensor.test")
+    assert sensor.attributes.get(ATTR_DISTANCE) == 18.388
+
+
+async def test_route_mode_fastest(hass, requests_mock_credentials_check):
+    """Test that route mode fastest works."""
+    origin = "38.902981,-77.048338"
+    destination = "39.042158,-77.119116"
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_CAR, TRAFFIC_MODE_ENABLED]
+    response_url = _build_mock_url(origin, destination, modes, APP_ID, APP_CODE, "now")
+    requests_mock_credentials_check.get(
+        response_url, text=load_fixture("here_travel_time/car_enabled_response.json")
+    )
+
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": origin.split(",")[0],
+            "origin_longitude": origin.split(",")[1],
+            "destination_latitude": destination.split(",")[0],
+            "destination_longitude": destination.split(",")[1],
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "traffic_mode": True,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+
+    sensor = hass.states.get("sensor.test")
+    assert sensor.attributes.get(ATTR_DISTANCE) == 23.381
+
+
+async def test_truck(hass, requests_mock_truck_response):
+    """Test that truck works."""
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": TRUCK_ORIGIN_LATITUDE,
+            "origin_longitude": TRUCK_ORIGIN_LONGITUDE,
+            "destination_latitude": TRUCK_DESTINATION_LATITUDE,
+            "destination_longitude": TRUCK_DESTINATION_LONGITUDE,
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "mode": TRAVEL_MODE_TRUCK,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+    sensor = hass.states.get("sensor.test")
+    _assert_truck_sensor(sensor)
+
+
+async def test_public_transport(hass, requests_mock_credentials_check):
+    """Test that publicTransport works."""
+    origin = "41.9798,-87.8801"
+    destination = "41.9043,-87.9216"
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_PUBLIC, TRAFFIC_MODE_DISABLED]
+    response_url = _build_mock_url(origin, destination, modes, APP_ID, APP_CODE, "now")
+    requests_mock_credentials_check.get(
+        response_url, text=load_fixture("here_travel_time/public_response.json")
+    )
+
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": origin.split(",")[0],
+            "origin_longitude": origin.split(",")[1],
+            "destination_latitude": destination.split(",")[0],
+            "destination_longitude": destination.split(",")[1],
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "mode": TRAVEL_MODE_PUBLIC,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+
+    sensor = hass.states.get("sensor.test")
+    assert sensor.state == "89"
+    assert sensor.attributes.get("unit_of_measurement") == UNIT_OF_MEASUREMENT
+
+    assert sensor.attributes.get(ATTR_ATTRIBUTION) is None
+    assert sensor.attributes.get(ATTR_DURATION) == 89.16666666666667
+    assert sensor.attributes.get(ATTR_DISTANCE) == 22.325
+    assert sensor.attributes.get(ATTR_ROUTE) == (
+        "332 - Palmer/Schiller; 332 - Cargo Rd./Delta Cargo; " "332 - Palmer/Schiller"
+    )
+    assert sensor.attributes.get(CONF_UNIT_SYSTEM) == "metric"
+    assert sensor.attributes.get(ATTR_DURATION_IN_TRAFFIC) == 89.16666666666667
+    assert sensor.attributes.get(ATTR_ORIGIN) == origin
+    assert sensor.attributes.get(ATTR_DESTINATION) == destination
+    assert sensor.attributes.get(ATTR_ORIGIN_NAME) == "Mannheim Rd"
+    assert sensor.attributes.get(ATTR_DESTINATION_NAME) == ""
+    assert sensor.attributes.get(CONF_MODE) == TRAVEL_MODE_PUBLIC
+    assert sensor.attributes.get(CONF_TRAFFIC_MODE) is False
+
+    assert sensor.attributes.get(ATTR_ICON) == ICON_PUBLIC
+
+
+async def test_public_transport_time_table(hass, requests_mock_credentials_check):
+    """Test that publicTransportTimeTable works."""
+    origin = "41.9798,-87.8801"
+    destination = "41.9043,-87.9216"
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_PUBLIC_TIME_TABLE, TRAFFIC_MODE_DISABLED]
+    response_url = _build_mock_url(origin, destination, modes, APP_ID, APP_CODE, "now")
+    requests_mock_credentials_check.get(
+        response_url,
+        text=load_fixture("here_travel_time/public_time_table_response.json"),
+    )
+
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": origin.split(",")[0],
+            "origin_longitude": origin.split(",")[1],
+            "destination_latitude": destination.split(",")[0],
+            "destination_longitude": destination.split(",")[1],
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "mode": TRAVEL_MODE_PUBLIC_TIME_TABLE,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+
+    sensor = hass.states.get("sensor.test")
+    assert sensor.state == "80"
+    assert sensor.attributes.get("unit_of_measurement") == UNIT_OF_MEASUREMENT
+
+    assert sensor.attributes.get(ATTR_ATTRIBUTION) is None
+    assert sensor.attributes.get(ATTR_DURATION) == 79.73333333333333
+    assert sensor.attributes.get(ATTR_DISTANCE) == 14.775
+    assert sensor.attributes.get(ATTR_ROUTE) == (
+        "330 - Archer/Harlem (Terminal); 309 - Elmhurst Metra Station"
+    )
+    assert sensor.attributes.get(CONF_UNIT_SYSTEM) == "metric"
+    assert sensor.attributes.get(ATTR_DURATION_IN_TRAFFIC) == 79.73333333333333
+    assert sensor.attributes.get(ATTR_ORIGIN) == origin
+    assert sensor.attributes.get(ATTR_DESTINATION) == destination
+    assert sensor.attributes.get(ATTR_ORIGIN_NAME) == "Mannheim Rd"
+    assert sensor.attributes.get(ATTR_DESTINATION_NAME) == ""
+    assert sensor.attributes.get(CONF_MODE) == TRAVEL_MODE_PUBLIC_TIME_TABLE
+    assert sensor.attributes.get(CONF_TRAFFIC_MODE) is False
+
+    assert sensor.attributes.get(ATTR_ICON) == ICON_PUBLIC
+
+
+async def test_pedestrian(hass, requests_mock_credentials_check):
+    """Test that pedestrian works."""
+    origin = "41.9798,-87.8801"
+    destination = "41.9043,-87.9216"
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_PEDESTRIAN, TRAFFIC_MODE_DISABLED]
+    response_url = _build_mock_url(origin, destination, modes, APP_ID, APP_CODE, "now")
+    requests_mock_credentials_check.get(
+        response_url, text=load_fixture("here_travel_time/pedestrian_response.json")
+    )
+
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": origin.split(",")[0],
+            "origin_longitude": origin.split(",")[1],
+            "destination_latitude": destination.split(",")[0],
+            "destination_longitude": destination.split(",")[1],
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "mode": TRAVEL_MODE_PEDESTRIAN,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+
+    sensor = hass.states.get("sensor.test")
+    assert sensor.state == "211"
+    assert sensor.attributes.get("unit_of_measurement") == UNIT_OF_MEASUREMENT
+
+    assert sensor.attributes.get(ATTR_ATTRIBUTION) is None
+    assert sensor.attributes.get(ATTR_DURATION) == 210.51666666666668
+    assert sensor.attributes.get(ATTR_DISTANCE) == 12.533
+    assert sensor.attributes.get(ATTR_ROUTE) == (
+        "Mannheim Rd; W Belmont Ave; Cullerton St; E Fullerton Ave; "
+        "La Porte Ave; E Palmer Ave; N Railroad Ave; W North Ave; "
+        "E North Ave; E Third St"
+    )
+    assert sensor.attributes.get(CONF_UNIT_SYSTEM) == "metric"
+    assert sensor.attributes.get(ATTR_DURATION_IN_TRAFFIC) == 210.51666666666668
+    assert sensor.attributes.get(ATTR_ORIGIN) == origin
+    assert sensor.attributes.get(ATTR_DESTINATION) == destination
+    assert sensor.attributes.get(ATTR_ORIGIN_NAME) == "Mannheim Rd"
+    assert sensor.attributes.get(ATTR_DESTINATION_NAME) == ""
+    assert sensor.attributes.get(CONF_MODE) == TRAVEL_MODE_PEDESTRIAN
+    assert sensor.attributes.get(CONF_TRAFFIC_MODE) is False
+
+    assert sensor.attributes.get(ATTR_ICON) == ICON_PEDESTRIAN
+
+
+async def test_bicycle(hass, requests_mock_credentials_check):
+    """Test that bicycle works."""
+    origin = "41.9798,-87.8801"
+    destination = "41.9043,-87.9216"
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_BICYCLE, TRAFFIC_MODE_DISABLED]
+    response_url = _build_mock_url(origin, destination, modes, APP_ID, APP_CODE, "now")
+    requests_mock_credentials_check.get(
+        response_url, text=load_fixture("here_travel_time/bike_response.json")
+    )
+
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": origin.split(",")[0],
+            "origin_longitude": origin.split(",")[1],
+            "destination_latitude": destination.split(",")[0],
+            "destination_longitude": destination.split(",")[1],
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "mode": TRAVEL_MODE_BICYCLE,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+
+    sensor = hass.states.get("sensor.test")
+    assert sensor.state == "55"
+    assert sensor.attributes.get("unit_of_measurement") == UNIT_OF_MEASUREMENT
+
+    assert sensor.attributes.get(ATTR_ATTRIBUTION) is None
+    assert sensor.attributes.get(ATTR_DURATION) == 54.86666666666667
+    assert sensor.attributes.get(ATTR_DISTANCE) == 12.613
+    assert sensor.attributes.get(ATTR_ROUTE) == (
+        "Mannheim Rd; W Belmont Ave; Cullerton St; N Landen Dr; "
+        "E Fullerton Ave; N Wolf Rd; W North Ave; N Clinton Ave; "
+        "E Third St; N Caroline Ave"
+    )
+    assert sensor.attributes.get(CONF_UNIT_SYSTEM) == "metric"
+    assert sensor.attributes.get(ATTR_DURATION_IN_TRAFFIC) == 54.86666666666667
+    assert sensor.attributes.get(ATTR_ORIGIN) == origin
+    assert sensor.attributes.get(ATTR_DESTINATION) == destination
+    assert sensor.attributes.get(ATTR_ORIGIN_NAME) == "Mannheim Rd"
+    assert sensor.attributes.get(ATTR_DESTINATION_NAME) == ""
+    assert sensor.attributes.get(CONF_MODE) == TRAVEL_MODE_BICYCLE
+    assert sensor.attributes.get(CONF_TRAFFIC_MODE) is False
+
+    assert sensor.attributes.get(ATTR_ICON) == ICON_BICYCLE
+
+
+async def test_location_zone(hass, requests_mock_truck_response):
+    """Test that origin/destination supplied by a zone works."""
+    utcnow = dt_util.utcnow()
+    # Patching 'utcnow' to gain more control over the timed update.
+    with patch("homeassistant.util.dt.utcnow", return_value=utcnow):
+        zone_config = {
+            "zone": [
+                {
+                    "name": "Destination",
+                    "latitude": TRUCK_DESTINATION_LATITUDE,
+                    "longitude": TRUCK_DESTINATION_LONGITUDE,
+                    "radius": 250,
+                    "passive": False,
+                },
+                {
+                    "name": "Origin",
+                    "latitude": TRUCK_ORIGIN_LATITUDE,
+                    "longitude": TRUCK_ORIGIN_LONGITUDE,
+                    "radius": 250,
+                    "passive": False,
+                },
+            ]
+        }
+        assert await async_setup_component(hass, "zone", zone_config)
+
+        config = {
+            DOMAIN: {
+                "platform": PLATFORM,
+                "name": "test",
+                "origin_entity_id": "zone.origin",
+                "destination_entity_id": "zone.destination",
+                "app_id": APP_ID,
+                "app_code": APP_CODE,
+                "mode": TRAVEL_MODE_TRUCK,
+            }
+        }
+        assert await async_setup_component(hass, DOMAIN, config)
+
+        sensor = hass.states.get("sensor.test")
+        _assert_truck_sensor(sensor)
+
+        # Test that update works more than once
+        async_fire_time_changed(hass, utcnow + SCAN_INTERVAL)
+        await hass.async_block_till_done()
+
+        sensor = hass.states.get("sensor.test")
+        _assert_truck_sensor(sensor)
+
+
+async def test_location_sensor(hass, requests_mock_truck_response):
+    """Test that origin/destination supplied by a sensor works."""
+    utcnow = dt_util.utcnow()
+    # Patching 'utcnow' to gain more control over the timed update.
+    with patch("homeassistant.util.dt.utcnow", return_value=utcnow):
+        hass.states.async_set(
+            "sensor.origin", ",".join([TRUCK_ORIGIN_LATITUDE, TRUCK_ORIGIN_LONGITUDE])
+        )
+        hass.states.async_set(
+            "sensor.destination",
+            ",".join([TRUCK_DESTINATION_LATITUDE, TRUCK_DESTINATION_LONGITUDE]),
+        )
+
+        config = {
+            DOMAIN: {
+                "platform": PLATFORM,
+                "name": "test",
+                "origin_entity_id": "sensor.origin",
+                "destination_entity_id": "sensor.destination",
+                "app_id": APP_ID,
+                "app_code": APP_CODE,
+                "mode": TRAVEL_MODE_TRUCK,
+            }
+        }
+        assert await async_setup_component(hass, DOMAIN, config)
+
+        sensor = hass.states.get("sensor.test")
+        _assert_truck_sensor(sensor)
+
+        # Test that update works more than once
+        async_fire_time_changed(hass, utcnow + SCAN_INTERVAL)
+        await hass.async_block_till_done()
+
+        sensor = hass.states.get("sensor.test")
+        _assert_truck_sensor(sensor)
+
+
+async def test_location_person(hass, requests_mock_truck_response):
+    """Test that origin/destination supplied by a person works."""
+    utcnow = dt_util.utcnow()
+    # Patching 'utcnow' to gain more control over the timed update.
+    with patch("homeassistant.util.dt.utcnow", return_value=utcnow):
+        hass.states.async_set(
+            "person.origin",
+            "unknown",
+            {
+                "latitude": float(TRUCK_ORIGIN_LATITUDE),
+                "longitude": float(TRUCK_ORIGIN_LONGITUDE),
+            },
+        )
+        hass.states.async_set(
+            "person.destination",
+            "unknown",
+            {
+                "latitude": float(TRUCK_DESTINATION_LATITUDE),
+                "longitude": float(TRUCK_DESTINATION_LONGITUDE),
+            },
+        )
+
+        config = {
+            DOMAIN: {
+                "platform": PLATFORM,
+                "name": "test",
+                "origin_entity_id": "person.origin",
+                "destination_entity_id": "person.destination",
+                "app_id": APP_ID,
+                "app_code": APP_CODE,
+                "mode": TRAVEL_MODE_TRUCK,
+            }
+        }
+        assert await async_setup_component(hass, DOMAIN, config)
+
+        sensor = hass.states.get("sensor.test")
+        _assert_truck_sensor(sensor)
+
+        # Test that update works more than once
+        async_fire_time_changed(hass, utcnow + SCAN_INTERVAL)
+        await hass.async_block_till_done()
+
+        sensor = hass.states.get("sensor.test")
+        _assert_truck_sensor(sensor)
+
+
+async def test_location_device_tracker(hass, requests_mock_truck_response):
+    """Test that origin/destination supplied by a device_tracker works."""
+    utcnow = dt_util.utcnow()
+    # Patching 'utcnow' to gain more control over the timed update.
+    with patch("homeassistant.util.dt.utcnow", return_value=utcnow):
+        hass.states.async_set(
+            "device_tracker.origin",
+            "unknown",
+            {
+                "latitude": float(TRUCK_ORIGIN_LATITUDE),
+                "longitude": float(TRUCK_ORIGIN_LONGITUDE),
+            },
+        )
+        hass.states.async_set(
+            "device_tracker.destination",
+            "unknown",
+            {
+                "latitude": float(TRUCK_DESTINATION_LATITUDE),
+                "longitude": float(TRUCK_DESTINATION_LONGITUDE),
+            },
+        )
+
+        config = {
+            DOMAIN: {
+                "platform": PLATFORM,
+                "name": "test",
+                "origin_entity_id": "device_tracker.origin",
+                "destination_entity_id": "device_tracker.destination",
+                "app_id": APP_ID,
+                "app_code": APP_CODE,
+                "mode": TRAVEL_MODE_TRUCK,
+            }
+        }
+        assert await async_setup_component(hass, DOMAIN, config)
+
+        sensor = hass.states.get("sensor.test")
+        _assert_truck_sensor(sensor)
+
+        # Test that update works more than once
+        async_fire_time_changed(hass, utcnow + SCAN_INTERVAL)
+        await hass.async_block_till_done()
+
+        sensor = hass.states.get("sensor.test")
+        _assert_truck_sensor(sensor)
+
+
+async def test_location_device_tracker_added_after_update(
+    hass, requests_mock_truck_response, caplog
+):
+    """Test that device_tracker added after first update works."""
+    caplog.set_level(logging.ERROR)
+    utcnow = dt_util.utcnow()
+    # Patching 'utcnow' to gain more control over the timed update.
+    with patch("homeassistant.util.dt.utcnow", return_value=utcnow):
+        config = {
+            DOMAIN: {
+                "platform": PLATFORM,
+                "name": "test",
+                "origin_entity_id": "device_tracker.origin",
+                "destination_entity_id": "device_tracker.destination",
+                "app_id": APP_ID,
+                "app_code": APP_CODE,
+                "mode": TRAVEL_MODE_TRUCK,
+            }
+        }
+        assert await async_setup_component(hass, DOMAIN, config)
+
+        sensor = hass.states.get("sensor.test")
+        assert len(caplog.records) == 2
+        assert "Unable to find entity" in caplog.text
+        caplog.clear()
+
+        # Device tracker appear after first update
+        hass.states.async_set(
+            "device_tracker.origin",
+            "unknown",
+            {
+                "latitude": float(TRUCK_ORIGIN_LATITUDE),
+                "longitude": float(TRUCK_ORIGIN_LONGITUDE),
+            },
+        )
+        hass.states.async_set(
+            "device_tracker.destination",
+            "unknown",
+            {
+                "latitude": float(TRUCK_DESTINATION_LATITUDE),
+                "longitude": float(TRUCK_DESTINATION_LONGITUDE),
+            },
+        )
+
+        # Test that update works more than once
+        async_fire_time_changed(hass, utcnow + SCAN_INTERVAL)
+        await hass.async_block_till_done()
+
+        sensor = hass.states.get("sensor.test")
+        _assert_truck_sensor(sensor)
+        assert len(caplog.records) == 0
+
+
+async def test_location_device_tracker_in_zone(
+    hass, requests_mock_truck_response, caplog
+):
+    """Test that device_tracker in zone uses device_tracker state works."""
+    caplog.set_level(logging.DEBUG)
+    zone_config = {
+        "zone": [
+            {
+                "name": "Origin",
+                "latitude": TRUCK_ORIGIN_LATITUDE,
+                "longitude": TRUCK_ORIGIN_LONGITUDE,
+                "radius": 250,
+                "passive": False,
+            }
+        ]
+    }
+    assert await async_setup_component(hass, "zone", zone_config)
+    hass.states.async_set(
+        "device_tracker.origin", "origin", {"latitude": None, "longitude": None}
+    )
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_entity_id": "device_tracker.origin",
+            "destination_latitude": TRUCK_DESTINATION_LATITUDE,
+            "destination_longitude": TRUCK_DESTINATION_LONGITUDE,
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "mode": TRAVEL_MODE_TRUCK,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+
+    sensor = hass.states.get("sensor.test")
+    _assert_truck_sensor(sensor)
+    assert ", getting zone location" in caplog.text
+
+
+async def test_route_not_found(hass, requests_mock_credentials_check, caplog):
+    """Test that route not found error is correctly handled."""
+    caplog.set_level(logging.ERROR)
+    origin = "52.516,13.3779"
+    destination = "47.013399,-10.171986"
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_CAR, TRAFFIC_MODE_DISABLED]
+    response_url = _build_mock_url(origin, destination, modes, APP_ID, APP_CODE, "now")
+    requests_mock_credentials_check.get(
+        response_url,
+        text=load_fixture("here_travel_time/routing_error_no_route_found.json"),
+    )
+
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": origin.split(",")[0],
+            "origin_longitude": origin.split(",")[1],
+            "destination_latitude": destination.split(",")[0],
+            "destination_longitude": destination.split(",")[1],
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+    assert len(caplog.records) == 1
+    assert NO_ROUTE_ERROR_MESSAGE in caplog.text
+
+
+async def test_pattern_origin(hass, caplog):
+    """Test that pattern matching the origin works."""
+    caplog.set_level(logging.ERROR)
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": "138.90",
+            "origin_longitude": "-77.04833",
+            "destination_latitude": CAR_DESTINATION_LATITUDE,
+            "destination_longitude": CAR_DESTINATION_LONGITUDE,
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+    assert len(caplog.records) == 1
+    assert "invalid latitude" in caplog.text
+
+
+async def test_pattern_destination(hass, caplog):
+    """Test that pattern matching the destination works."""
+    caplog.set_level(logging.ERROR)
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": CAR_ORIGIN_LATITUDE,
+            "origin_longitude": CAR_ORIGIN_LONGITUDE,
+            "destination_latitude": "139.0",
+            "destination_longitude": "-77.1",
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+    assert len(caplog.records) == 1
+    assert "invalid latitude" in caplog.text
+
+
+async def test_invalid_credentials(hass, requests_mock, caplog):
+    """Test that invalid credentials error is correctly handled."""
+    caplog.set_level(logging.ERROR)
+    modes = [ROUTE_MODE_FASTEST, TRAVEL_MODE_CAR, TRAFFIC_MODE_DISABLED]
+    response_url = _build_mock_url(
+        ",".join([CAR_ORIGIN_LATITUDE, CAR_ORIGIN_LONGITUDE]),
+        ",".join([CAR_DESTINATION_LATITUDE, CAR_DESTINATION_LONGITUDE]),
+        modes,
+        APP_ID,
+        APP_CODE,
+        "now",
+    )
+    requests_mock.get(
+        response_url,
+        text=load_fixture("here_travel_time/routing_error_invalid_credentials.json"),
+    )
+
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": CAR_ORIGIN_LATITUDE,
+            "origin_longitude": CAR_ORIGIN_LONGITUDE,
+            "destination_latitude": CAR_DESTINATION_LATITUDE,
+            "destination_longitude": CAR_DESTINATION_LONGITUDE,
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+    assert len(caplog.records) == 1
+    assert "Invalid credentials" in caplog.text
+
+
+async def test_attribution(hass, requests_mock_credentials_check):
+    """Test that attributions are correctly displayed."""
+    origin = "50.037751372637686,14.39233448220898"
+    destination = "50.07993838201255,14.42582157361062"
+    modes = [ROUTE_MODE_SHORTEST, TRAVEL_MODE_PUBLIC_TIME_TABLE, TRAFFIC_MODE_ENABLED]
+    response_url = _build_mock_url(origin, destination, modes, APP_ID, APP_CODE, "now")
+    requests_mock_credentials_check.get(
+        response_url, text=load_fixture("here_travel_time/attribution_response.json")
+    )
+
+    config = {
+        DOMAIN: {
+            "platform": PLATFORM,
+            "name": "test",
+            "origin_latitude": origin.split(",")[0],
+            "origin_longitude": origin.split(",")[1],
+            "destination_latitude": destination.split(",")[0],
+            "destination_longitude": destination.split(",")[1],
+            "app_id": APP_ID,
+            "app_code": APP_CODE,
+            "traffic_mode": True,
+            "route_mode": ROUTE_MODE_SHORTEST,
+            "mode": TRAVEL_MODE_PUBLIC_TIME_TABLE,
+        }
+    }
+    assert await async_setup_component(hass, DOMAIN, config)
+    sensor = hass.states.get("sensor.test")
+    assert (
+        sensor.attributes.get(ATTR_ATTRIBUTION)
+        == "With the support of HERE Technologies. All information is provided without warranty of any kind."
+    )
diff --git a/tests/fixtures/here_travel_time/attribution_response.json b/tests/fixtures/here_travel_time/attribution_response.json
new file mode 100644
index 00000000000..9b682f6c51f
--- /dev/null
+++ b/tests/fixtures/here_travel_time/attribution_response.json
@@ -0,0 +1,276 @@
+{
+    "response": {
+        "metaInfo": {
+            "timestamp": "2019-09-21T15:17:31Z",
+            "mapVersion": "8.30.100.154",
+            "moduleVersion": "7.2.201937-5251",
+            "interfaceVersion": "2.6.70",
+            "availableMapVersion": [
+                "8.30.100.154"
+            ]
+        },
+        "route": [
+            {
+                "waypoint": [
+                    {
+                        "linkId": "+565790671",
+                        "mappedPosition": {
+                            "latitude": 50.0378591,
+                            "longitude": 14.3924721
+                        },
+                        "originalPosition": {
+                            "latitude": 50.0377513,
+                            "longitude": 14.3923344
+                        },
+                        "type": "stopOver",
+                        "spot": 0.3,
+                        "sideOfStreet": "left",
+                        "mappedRoadName": "V Bokách III",
+                        "label": "V Bokách III",
+                        "shapeIndex": 0,
+                        "source": "user"
+                    },
+                    {
+                        "linkId": "+748931502",
+                        "mappedPosition": {
+                            "latitude": 50.0798786,
+                            "longitude": 14.4260037
+                        },
+                        "originalPosition": {
+                            "latitude": 50.0799383,
+                            "longitude": 14.4258216
+                        },
+                        "type": "stopOver",
+                        "spot": 1.0,
+                        "sideOfStreet": "left",
+                        "mappedRoadName": "Štěpánská",
+                        "label": "Štěpánská",
+                        "shapeIndex": 116,
+                        "source": "user"
+                    }
+                ],
+                "mode": {
+                    "type": "shortest",
+                    "transportModes": [
+                        "publicTransportTimeTable"
+                    ],
+                    "trafficMode": "enabled",
+                    "feature": []
+                },
+                "leg": [
+                    {
+                        "start": {
+                            "linkId": "+565790671",
+                            "mappedPosition": {
+                                "latitude": 50.0378591,
+                                "longitude": 14.3924721
+                            },
+                            "originalPosition": {
+                                "latitude": 50.0377513,
+                                "longitude": 14.3923344
+                            },
+                            "type": "stopOver",
+                            "spot": 0.3,
+                            "sideOfStreet": "left",
+                            "mappedRoadName": "V Bokách III",
+                            "label": "V Bokách III",
+                            "shapeIndex": 0,
+                            "source": "user"
+                        },
+                        "end": {
+                            "linkId": "+748931502",
+                            "mappedPosition": {
+                                "latitude": 50.0798786,
+                                "longitude": 14.4260037
+                            },
+                            "originalPosition": {
+                                "latitude": 50.0799383,
+                                "longitude": 14.4258216
+                            },
+                            "type": "stopOver",
+                            "spot": 1.0,
+                            "sideOfStreet": "left",
+                            "mappedRoadName": "Štěpánská",
+                            "label": "Štěpánská",
+                            "shapeIndex": 116,
+                            "source": "user"
+                        },
+                        "length": 7835,
+                        "travelTime": 2413,
+                        "maneuver": [
+                            {
+                                "position": {
+                                    "latitude": 50.0378591,
+                                    "longitude": 14.3924721
+                                },
+                                "instruction": "Head <span class=\"heading\">northwest</span> on <span class=\"street\">Kosořská</span>. <span class=\"distance-description\">Go for <span class=\"length\">28 m</span>.</span>",
+                                "travelTime": 32,
+                                "length": 28,
+                                "id": "M1",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0380039,
+                                    "longitude": 14.3921542
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Kosořská</span>. <span class=\"distance-description\">Go for <span class=\"length\">24 m</span>.</span>",
+                                "travelTime": 24,
+                                "length": 24,
+                                "id": "M2",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0380039,
+                                    "longitude": 14.3918109
+                                },
+                                "instruction": "Take the street on the <span class=\"direction\">left</span>, <span class=\"next-street\">Slivenecká</span>. <span class=\"distance-description\">Go for <span class=\"length\">343 m</span>.</span>",
+                                "travelTime": 354,
+                                "length": 343,
+                                "id": "M3",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0376499,
+                                    "longitude": 14.3871975
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Slivenecká</span>. <span class=\"distance-description\">Go for <span class=\"length\">64 m</span>.</span>",
+                                "travelTime": 72,
+                                "length": 64,
+                                "id": "M4",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0373602,
+                                    "longitude": 14.3879807
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">Slivenecká</span>. <span class=\"distance-description\">Go for <span class=\"length\">91 m</span>.</span>",
+                                "travelTime": 95,
+                                "length": 91,
+                                "id": "M5",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0365448,
+                                    "longitude": 14.3878305
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">K Barrandovu</span>. <span class=\"distance-description\">Go for <span class=\"length\">124 m</span>.</span>",
+                                "travelTime": 126,
+                                "length": 124,
+                                "id": "M6",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0363168,
+                                    "longitude": 14.3894618
+                                },
+                                "instruction": "Go to the <span class=\"company\">Tram</span> station <span class=\"station\">Geologicka</span> and take the <span class=\"transit\">rail</span> <span class=\"line\">5</span> toward <span class=\"destination\">Ústřední dílny DP</span>. <span class=\"distance-description\">Follow for <span class=\"stops\">13 stations</span>.</span>",
+                                "travelTime": 1440,
+                                "length": 6911,
+                                "id": "M7",
+                                "stopName": "Geologicka",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0800508,
+                                    "longitude": 14.423403
+                                },
+                                "instruction": "Get off at <span class=\"station\">Vodickova</span>.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M8",
+                                "stopName": "Vodickova",
+                                "nextRoadName": "Vodičkova",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0800508,
+                                    "longitude": 14.423403
+                                },
+                                "instruction": "Head <span class=\"heading\">northeast</span> on <span class=\"street\">Vodičkova</span>. <span class=\"distance-description\">Go for <span class=\"length\">65 m</span>.</span>",
+                                "travelTime": 74,
+                                "length": 65,
+                                "id": "M9",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0804901,
+                                    "longitude": 14.4239759
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">V Jámě</span>. <span class=\"distance-description\">Go for <span class=\"length\">163 m</span>.</span>",
+                                "travelTime": 174,
+                                "length": 163,
+                                "id": "M10",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0796962,
+                                    "longitude": 14.4258857
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Štěpánská</span>. <span class=\"distance-description\">Go for <span class=\"length\">22 m</span>.</span>",
+                                "travelTime": 22,
+                                "length": 22,
+                                "id": "M11",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 50.0798786,
+                                    "longitude": 14.4260037
+                                },
+                                "instruction": "Arrive at <span class=\"street\">Štěpánská</span>. Your destination is on the left.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M12",
+                                "_type": "PrivateTransportManeuverType"
+                            }
+                        ]
+                    }
+                ],
+                "publicTransportLine": [
+                    {
+                        "lineName": "5",
+                        "lineForeground": "#F5ADCE",
+                        "lineBackground": "#F5ADCE",
+                        "companyName": "HERE Technologies",
+                        "destination": "Ústřední dílny DP",
+                        "type": "railLight",
+                        "id": "L1"
+                    }
+                ],
+                "summary": {
+                    "distance": 7835,
+                    "baseTime": 2413,
+                    "flags": [
+                        "noThroughRoad",
+                        "builtUpArea"
+                    ],
+                    "text": "The trip takes <span class=\"length\">7.8 km</span> and <span class=\"time\">40 mins</span>.",
+                    "travelTime": 2413,
+                    "departure": "2019-09-21T17:16:17+02:00",
+                    "timetableExpiration": "2019-09-21T00:00:00Z",
+                    "_type": "PublicTransportRouteSummaryType"
+                }
+            }
+        ],
+        "language": "en-us",
+        "sourceAttribution": {
+            "attribution": "With the support of <span class=\"company\"><a href=\"https://transit.api.here.com/r?appId=Mt1bOYh3m9uxE7r3wuUx&u=https://wego.here.com\">HERE Technologies</a></span>. All information is provided without warranty of any kind.",
+            "supplier": [
+                {
+                    "title": "HERE Technologies",
+                    "href": "https://transit.api.here.com/r?appId=Mt1bOYh3m9uxE7r3wuUx&u=https://wego.here.com"
+                }
+            ]
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/fixtures/here_travel_time/bike_response.json b/tests/fixtures/here_travel_time/bike_response.json
new file mode 100644
index 00000000000..a3af39129d0
--- /dev/null
+++ b/tests/fixtures/here_travel_time/bike_response.json
@@ -0,0 +1,274 @@
+{
+    "response": {
+        "metaInfo": {
+            "timestamp": "2019-07-24T10:17:40Z",
+            "mapVersion": "8.30.98.154",
+            "moduleVersion": "7.2.201929-4522",
+            "interfaceVersion": "2.6.64",
+            "availableMapVersion": [
+                "8.30.98.154"
+            ]
+        },
+        "route": [
+            {
+                "waypoint": [
+                    {
+                        "linkId": "-1230414527",
+                        "mappedPosition": {
+                            "latitude": 41.9797859,
+                            "longitude": -87.8790879
+                        },
+                        "originalPosition": {
+                            "latitude": 41.9798,
+                            "longitude": -87.8801
+                        },
+                        "type": "stopOver",
+                        "spot": 0.5079365,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "Mannheim Rd",
+                        "label": "Mannheim Rd - US-12",
+                        "shapeIndex": 0,
+                        "source": "user"
+                    },
+                    {
+                        "linkId": "+924115108",
+                        "mappedPosition": {
+                            "latitude": 41.90413,
+                            "longitude": -87.9223502
+                        },
+                        "originalPosition": {
+                            "latitude": 41.9043,
+                            "longitude": -87.9216001
+                        },
+                        "type": "stopOver",
+                        "spot": 0.1925926,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "",
+                        "label": "",
+                        "shapeIndex": 87,
+                        "source": "user"
+                    }
+                ],
+                "mode": {
+                    "type": "fastest",
+                    "transportModes": [
+                        "bicycle"
+                    ],
+                    "trafficMode": "enabled",
+                    "feature": []
+                },
+                "leg": [
+                    {
+                        "start": {
+                            "linkId": "-1230414527",
+                            "mappedPosition": {
+                                "latitude": 41.9797859,
+                                "longitude": -87.8790879
+                            },
+                            "originalPosition": {
+                                "latitude": 41.9798,
+                                "longitude": -87.8801
+                            },
+                            "type": "stopOver",
+                            "spot": 0.5079365,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "Mannheim Rd",
+                            "label": "Mannheim Rd - US-12",
+                            "shapeIndex": 0,
+                            "source": "user"
+                        },
+                        "end": {
+                            "linkId": "+924115108",
+                            "mappedPosition": {
+                                "latitude": 41.90413,
+                                "longitude": -87.9223502
+                            },
+                            "originalPosition": {
+                                "latitude": 41.9043,
+                                "longitude": -87.9216001
+                            },
+                            "type": "stopOver",
+                            "spot": 0.1925926,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "",
+                            "label": "",
+                            "shapeIndex": 87,
+                            "source": "user"
+                        },
+                        "length": 12613,
+                        "travelTime": 3292,
+                        "maneuver": [
+                            {
+                                "position": {
+                                    "latitude": 41.9797859,
+                                    "longitude": -87.8790879
+                                },
+                                "instruction": "Head <span class=\"heading\">south</span> on <span class=\"street\">Mannheim Rd</span> <span class=\"number\">(US-12/US-45)</span>. <span class=\"distance-description\">Go for <span class=\"length\">2.6 km</span>.</span>",
+                                "travelTime": 646,
+                                "length": 2648,
+                                "id": "M1",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9579244,
+                                    "longitude": -87.8838551
+                                },
+                                "instruction": "Keep <span class=\"direction\">left</span> onto <span class=\"next-street\">Mannheim Rd</span> <span class=\"number\">(US-12/US-45)</span>. <span class=\"distance-description\">Go for <span class=\"length\">2.4 km</span>.</span>",
+                                "travelTime": 621,
+                                "length": 2427,
+                                "id": "M2",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9364238,
+                                    "longitude": -87.8849387
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">W Belmont Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">595 m</span>.</span>",
+                                "travelTime": 158,
+                                "length": 595,
+                                "id": "M3",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9362521,
+                                    "longitude": -87.8921163
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Cullerton St</span>. <span class=\"distance-description\">Go for <span class=\"length\">669 m</span>.</span>",
+                                "travelTime": 180,
+                                "length": 669,
+                                "id": "M4",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9305658,
+                                    "longitude": -87.8932428
+                                },
+                                "instruction": "Continue on <span class=\"next-street\">N Landen Dr</span>. <span class=\"distance-description\">Go for <span class=\"length\">976 m</span>.</span>",
+                                "travelTime": 246,
+                                "length": 976,
+                                "id": "M5",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9217896,
+                                    "longitude": -87.8928781
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">E Fullerton Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">904 m</span>.</span>",
+                                "travelTime": 238,
+                                "length": 904,
+                                "id": "M6",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.921618,
+                                    "longitude": -87.9038107
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">N Wolf Rd</span>. <span class=\"distance-description\">Go for <span class=\"length\">1.6 km</span>.</span>",
+                                "travelTime": 417,
+                                "length": 1604,
+                                "id": "M7",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.907177,
+                                    "longitude": -87.9032314
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">W North Ave</span> <span class=\"number\">(IL-64)</span>. <span class=\"distance-description\">Go for <span class=\"length\">2.0 km</span>.</span>",
+                                "travelTime": 574,
+                                "length": 2031,
+                                "id": "M8",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9065225,
+                                    "longitude": -87.9277039
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">N Clinton Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">275 m</span>.</span>",
+                                "travelTime": 78,
+                                "length": 275,
+                                "id": "M9",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9040549,
+                                    "longitude": -87.9277253
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">E Third St</span>. <span class=\"distance-description\">Go for <span class=\"length\">249 m</span>.</span>",
+                                "travelTime": 63,
+                                "length": 249,
+                                "id": "M10",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9040334,
+                                    "longitude": -87.9247105
+                                },
+                                "instruction": "Continue on <span class=\"next-street\">N Caroline Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">96 m</span>.</span>",
+                                "travelTime": 37,
+                                "length": 96,
+                                "id": "M11",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9038832,
+                                    "longitude": -87.9236054
+                                },
+                                "instruction": "Turn <span class=\"direction\">slightly left</span>. <span class=\"distance-description\">Go for <span class=\"length\">113 m</span>.</span>",
+                                "travelTime": 28,
+                                "length": 113,
+                                "id": "M12",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9039047,
+                                    "longitude": -87.9222536
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span>. <span class=\"distance-description\">Go for <span class=\"length\">26 m</span>.</span>",
+                                "travelTime": 6,
+                                "length": 26,
+                                "id": "M13",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.90413,
+                                    "longitude": -87.9223502
+                                },
+                                "instruction": "Arrive at your destination on the right.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M14",
+                                "_type": "PrivateTransportManeuverType"
+                            }
+                        ]
+                    }
+                ],
+                "summary": {
+                    "distance": 12613,
+                    "baseTime": 3292,
+                    "flags": [
+                        "noThroughRoad",
+                        "builtUpArea",
+                        "park"
+                    ],
+                    "text": "The trip takes <span class=\"length\">12.6 km</span> and <span class=\"time\">55 mins</span>.",
+                    "travelTime": 3292,
+                    "_type": "RouteSummaryType"
+                }
+            }
+        ],
+        "language": "en-us"
+    }
+}
\ No newline at end of file
diff --git a/tests/fixtures/here_travel_time/car_enabled_response.json b/tests/fixtures/here_travel_time/car_enabled_response.json
new file mode 100644
index 00000000000..08da738f046
--- /dev/null
+++ b/tests/fixtures/here_travel_time/car_enabled_response.json
@@ -0,0 +1,298 @@
+{
+    "response": {
+        "metaInfo": {
+            "timestamp": "2019-07-21T21:21:31Z",
+            "mapVersion": "8.30.98.154",
+            "moduleVersion": "7.2.201928-4478",
+            "interfaceVersion": "2.6.64",
+            "availableMapVersion": [
+                "8.30.98.154"
+            ]
+        },
+        "route": [
+            {
+                "waypoint": [
+                    {
+                        "linkId": "-1128310200",
+                        "mappedPosition": {
+                            "latitude": 38.9026523,
+                            "longitude": -77.048338
+                        },
+                        "originalPosition": {
+                            "latitude": 38.9029809,
+                            "longitude": -77.048338
+                        },
+                        "type": "stopOver",
+                        "spot": 0.3538462,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "K St NW",
+                        "label": "K St NW",
+                        "shapeIndex": 0,
+                        "source": "user"
+                    },
+                    {
+                        "linkId": "-18459081",
+                        "mappedPosition": {
+                            "latitude": 39.0422511,
+                            "longitude": -77.1193526
+                        },
+                        "originalPosition": {
+                            "latitude": 39.042158,
+                            "longitude": -77.119116
+                        },
+                        "type": "stopOver",
+                        "spot": 0.7253521,
+                        "sideOfStreet": "left",
+                        "mappedRoadName": "Commonwealth Dr",
+                        "label": "Commonwealth Dr",
+                        "shapeIndex": 283,
+                        "source": "user"
+                    }
+                ],
+                "mode": {
+                    "type": "fastest",
+                    "transportModes": [
+                        "car"
+                    ],
+                    "trafficMode": "enabled",
+                    "feature": []
+                },
+                "leg": [
+                    {
+                        "start": {
+                            "linkId": "-1128310200",
+                            "mappedPosition": {
+                                "latitude": 38.9026523,
+                                "longitude": -77.048338
+                            },
+                            "originalPosition": {
+                                "latitude": 38.9029809,
+                                "longitude": -77.048338
+                            },
+                            "type": "stopOver",
+                            "spot": 0.3538462,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "K St NW",
+                            "label": "K St NW",
+                            "shapeIndex": 0,
+                            "source": "user"
+                        },
+                        "end": {
+                            "linkId": "-18459081",
+                            "mappedPosition": {
+                                "latitude": 39.0422511,
+                                "longitude": -77.1193526
+                            },
+                            "originalPosition": {
+                                "latitude": 39.042158,
+                                "longitude": -77.119116
+                            },
+                            "type": "stopOver",
+                            "spot": 0.7253521,
+                            "sideOfStreet": "left",
+                            "mappedRoadName": "Commonwealth Dr",
+                            "label": "Commonwealth Dr",
+                            "shapeIndex": 283,
+                            "source": "user"
+                        },
+                        "length": 23381,
+                        "travelTime": 1817,
+                        "maneuver": [
+                            {
+                                "position": {
+                                    "latitude": 38.9026523,
+                                    "longitude": -77.048338
+                                },
+                                "instruction": "Head toward <span class=\"toward_street\">22nd St NW</span> on <span class=\"street\">K St NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">140 m</span>.</span>",
+                                "travelTime": 36,
+                                "length": 140,
+                                "id": "M1",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9027703,
+                                    "longitude": -77.0494902
+                                },
+                                "instruction": "Take the <span class=\"exit\">3rd exit</span> from Washington Cir NW roundabout onto <span class=\"next-street\">K St NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">325 m</span>.</span>",
+                                "travelTime": 81,
+                                "length": 325,
+                                "id": "M2",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9026523,
+                                    "longitude": -77.0529449
+                                },
+                                "instruction": "Keep <span class=\"direction\">left</span> onto <span class=\"next-street\">K St NW</span> <span class=\"number\">(US-29)</span>. <span class=\"distance-description\">Go for <span class=\"length\">201 m</span>.</span>",
+                                "travelTime": 29,
+                                "length": 201,
+                                "id": "M3",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9025235,
+                                    "longitude": -77.0552516
+                                },
+                                "instruction": "Keep <span class=\"direction\">right</span> onto <span class=\"next-street\">Whitehurst Fwy</span> <span class=\"number\">(US-29)</span>. <span class=\"distance-description\">Go for <span class=\"length\">1.4 km</span>.</span>",
+                                "travelTime": 143,
+                                "length": 1381,
+                                "id": "M4",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9050448,
+                                    "longitude": -77.0701969
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">M St NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">784 m</span>.</span>",
+                                "travelTime": 80,
+                                "length": 784,
+                                "id": "M5",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9060318,
+                                    "longitude": -77.0790696
+                                },
+                                "instruction": "Turn <span class=\"direction\">slightly left</span> onto <span class=\"next-street\">Canal Rd NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">4.2 km</span>.</span>",
+                                "travelTime": 287,
+                                "length": 4230,
+                                "id": "M6",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9303219,
+                                    "longitude": -77.1117926
+                                },
+                                "instruction": "Continue on <span class=\"next-street\">Clara Barton Pkwy</span>. <span class=\"distance-description\">Go for <span class=\"length\">844 m</span>.</span>",
+                                "travelTime": 55,
+                                "length": 844,
+                                "id": "M7",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9368558,
+                                    "longitude": -77.1166742
+                                },
+                                "instruction": "Continue on <span class=\"next-street\">Clara Barton Pkwy</span>. <span class=\"distance-description\">Go for <span class=\"length\">4.7 km</span>.</span>",
+                                "travelTime": 294,
+                                "length": 4652,
+                                "id": "M8",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9706838,
+                                    "longitude": -77.1461463
+                                },
+                                "instruction": "Keep <span class=\"direction\">right</span> onto <span class=\"next-street\">Cabin John Pkwy N</span> toward <span class=\"sign\"><span lang=\"en\">I-495 N</span></span>. <span class=\"distance-description\">Go for <span class=\"length\">2.1 km</span>.</span>",
+                                "travelTime": 90,
+                                "length": 2069,
+                                "id": "M9",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9858222,
+                                    "longitude": -77.1571326
+                                },
+                                "instruction": "Take <span class=\"direction\">left</span> ramp onto <span class=\"number\">I-495 N</span> <span class=\"next-street\">(Capital Beltway)</span>. <span class=\"distance-description\">Go for <span class=\"length\">2.9 km</span>.</span>",
+                                "travelTime": 129,
+                                "length": 2890,
+                                "id": "M10",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 39.0104449,
+                                    "longitude": -77.1508026
+                                },
+                                "instruction": "Keep <span class=\"direction\">left</span> onto <span class=\"number\">I-270-SPUR</span> toward <span class=\"sign\"><span lang=\"en\">I-270</span>/<span lang=\"en\">Rockville</span>/<span lang=\"en\">Frederick</span></span>. <span class=\"distance-description\">Go for <span class=\"length\">1.1 km</span>.</span>",
+                                "travelTime": 48,
+                                "length": 1136,
+                                "id": "M11",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 39.0192747,
+                                    "longitude": -77.144773
+                                },
+                                "instruction": "Take exit <span class=\"exit\">1</span> toward <span class=\"sign\"><span lang=\"en\">Democracy Blvd</span>/<span lang=\"en\">Old Georgetown Rd</span>/<span lang=\"en\">MD-187</span></span> onto <span class=\"next-street\">Democracy Blvd</span>. <span class=\"distance-description\">Go for <span class=\"length\">1.8 km</span>.</span>",
+                                "travelTime": 205,
+                                "length": 1818,
+                                "id": "M12",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 39.0247464,
+                                    "longitude": -77.1253431
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Old Georgetown Rd</span> <span class=\"number\">(MD-187)</span>. <span class=\"distance-description\">Go for <span class=\"length\">2.3 km</span>.</span>",
+                                "travelTime": 230,
+                                "length": 2340,
+                                "id": "M13",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 39.0447772,
+                                    "longitude": -77.1203649
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">Nicholson Ln</span>. <span class=\"distance-description\">Go for <span class=\"length\">208 m</span>.</span>",
+                                "travelTime": 31,
+                                "length": 208,
+                                "id": "M14",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 39.0448952,
+                                    "longitude": -77.1179724
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">Commonwealth Dr</span>. <span class=\"distance-description\">Go for <span class=\"length\">341 m</span>.</span>",
+                                "travelTime": 75,
+                                "length": 341,
+                                "id": "M15",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 39.0422511,
+                                    "longitude": -77.1193526
+                                },
+                                "instruction": "Arrive at <span class=\"street\">Commonwealth Dr</span>. Your destination is on the left.",
+                                "travelTime": 4,
+                                "length": 22,
+                                "id": "M16",
+                                "_type": "PrivateTransportManeuverType"
+                            }
+                        ]
+                    }
+                ],
+                "summary": {
+                    "distance": 23381,
+                    "trafficTime": 1782,
+                    "baseTime": 1712,
+                    "flags": [
+                        "noThroughRoad",
+                        "motorway",
+                        "builtUpArea",
+                        "park"
+                    ],
+                    "text": "The trip takes <span class=\"length\">23.4 km</span> and <span class=\"time\">30 mins</span>.",
+                    "travelTime": 1782,
+                    "_type": "RouteSummaryType"
+                }
+            }
+        ],
+        "language": "en-us"
+    }
+}
\ No newline at end of file
diff --git a/tests/fixtures/here_travel_time/car_response.json b/tests/fixtures/here_travel_time/car_response.json
new file mode 100644
index 00000000000..bda8454f3f3
--- /dev/null
+++ b/tests/fixtures/here_travel_time/car_response.json
@@ -0,0 +1,299 @@
+{
+    "response": {
+        "metaInfo": {
+            "timestamp": "2019-07-19T07:38:39Z",
+            "mapVersion": "8.30.98.154",
+            "moduleVersion": "7.2.201928-4446",
+            "interfaceVersion": "2.6.64",
+            "availableMapVersion": [
+                "8.30.98.154"
+            ]
+        },
+        "route": [
+            {
+                "waypoint": [
+                    {
+                        "linkId": "+732182239",
+                        "mappedPosition": {
+                            "latitude": 38.9,
+                            "longitude": -77.0488358
+                        },
+                        "originalPosition": {
+                            "latitude": 38.9,
+                            "longitude": -77.0483301
+                        },
+                        "type": "stopOver",
+                        "spot": 0.4946237,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "22nd St NW",
+                        "label": "22nd St NW",
+                        "shapeIndex": 0,
+                        "source": "user"
+                    },
+                    {
+                        "linkId": "+942865877",
+                        "mappedPosition": {
+                            "latitude": 38.9999735,
+                            "longitude": -77.100141
+                        },
+                        "originalPosition": {
+                            "latitude": 38.9999999,
+                            "longitude": -77.1000001
+                        },
+                        "type": "stopOver",
+                        "spot": 1,
+                        "sideOfStreet": "left",
+                        "mappedRoadName": "Service Rd S",
+                        "label": "Service Rd S",
+                        "shapeIndex": 279,
+                        "source": "user"
+                    }
+                ],
+                "mode": {
+                    "type": "fastest",
+                    "transportModes": [
+                        "car"
+                    ],
+                    "trafficMode": "enabled",
+                    "feature": []
+                },
+                "leg": [
+                    {
+                        "start": {
+                            "linkId": "+732182239",
+                            "mappedPosition": {
+                                "latitude": 38.9,
+                                "longitude": -77.0488358
+                            },
+                            "originalPosition": {
+                                "latitude": 38.9,
+                                "longitude": -77.0483301
+                            },
+                            "type": "stopOver",
+                            "spot": 0.4946237,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "22nd St NW",
+                            "label": "22nd St NW",
+                            "shapeIndex": 0,
+                            "source": "user"
+                        },
+                        "end": {
+                            "linkId": "+942865877",
+                            "mappedPosition": {
+                                "latitude": 38.9999735,
+                                "longitude": -77.100141
+                            },
+                            "originalPosition": {
+                                "latitude": 38.9999999,
+                                "longitude": -77.1000001
+                            },
+                            "type": "stopOver",
+                            "spot": 1,
+                            "sideOfStreet": "left",
+                            "mappedRoadName": "Service Rd S",
+                            "label": "Service Rd S",
+                            "shapeIndex": 279,
+                            "source": "user"
+                        },
+                        "length": 23903,
+                        "travelTime": 1884,
+                        "maneuver": [
+                            {
+                                "position": {
+                                    "latitude": 38.9,
+                                    "longitude": -77.0488358
+                                },
+                                "instruction": "Head toward <span class=\"toward_street\">I St NW</span> on <span class=\"street\">22nd St NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">279 m</span>.</span>",
+                                "travelTime": 95,
+                                "length": 279,
+                                "id": "M1",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9021051,
+                                    "longitude": -77.048825
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> toward <span class=\"sign\">Pennsylvania Ave NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">71 m</span>.</span>",
+                                "travelTime": 21,
+                                "length": 71,
+                                "id": "M2",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.902545,
+                                    "longitude": -77.0494151
+                                },
+                                "instruction": "Take the <span class=\"exit\">3rd exit</span> from Washington Cir NW roundabout onto <span class=\"next-street\">K St NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">352 m</span>.</span>",
+                                "travelTime": 90,
+                                "length": 352,
+                                "id": "M3",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9026523,
+                                    "longitude": -77.0529449
+                                },
+                                "instruction": "Keep <span class=\"direction\">left</span> onto <span class=\"next-street\">K St NW</span> <span class=\"number\">(US-29)</span>. <span class=\"distance-description\">Go for <span class=\"length\">201 m</span>.</span>",
+                                "travelTime": 30,
+                                "length": 201,
+                                "id": "M4",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9025235,
+                                    "longitude": -77.0552516
+                                },
+                                "instruction": "Keep <span class=\"direction\">right</span> onto <span class=\"next-street\">Whitehurst Fwy</span> <span class=\"number\">(US-29)</span>. <span class=\"distance-description\">Go for <span class=\"length\">1.4 km</span>.</span>",
+                                "travelTime": 131,
+                                "length": 1381,
+                                "id": "M5",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9050448,
+                                    "longitude": -77.0701969
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">M St NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">784 m</span>.</span>",
+                                "travelTime": 78,
+                                "length": 784,
+                                "id": "M6",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9060318,
+                                    "longitude": -77.0790696
+                                },
+                                "instruction": "Turn <span class=\"direction\">slightly left</span> onto <span class=\"next-street\">Canal Rd NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">4.2 km</span>.</span>",
+                                "travelTime": 277,
+                                "length": 4230,
+                                "id": "M7",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9303219,
+                                    "longitude": -77.1117926
+                                },
+                                "instruction": "Continue on <span class=\"next-street\">Clara Barton Pkwy</span>. <span class=\"distance-description\">Go for <span class=\"length\">844 m</span>.</span>",
+                                "travelTime": 55,
+                                "length": 844,
+                                "id": "M8",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9368558,
+                                    "longitude": -77.1166742
+                                },
+                                "instruction": "Continue on <span class=\"next-street\">Clara Barton Pkwy</span>. <span class=\"distance-description\">Go for <span class=\"length\">4.7 km</span>.</span>",
+                                "travelTime": 298,
+                                "length": 4652,
+                                "id": "M9",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9706838,
+                                    "longitude": -77.1461463
+                                },
+                                "instruction": "Keep <span class=\"direction\">right</span> onto <span class=\"next-street\">Cabin John Pkwy N</span> toward <span class=\"sign\"><span lang=\"en\">I-495 N</span></span>. <span class=\"distance-description\">Go for <span class=\"length\">2.1 km</span>.</span>",
+                                "travelTime": 91,
+                                "length": 2069,
+                                "id": "M10",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9858222,
+                                    "longitude": -77.1571326
+                                },
+                                "instruction": "Take <span class=\"direction\">left</span> ramp onto <span class=\"number\">I-495 N</span> <span class=\"next-street\">(Capital Beltway)</span>. <span class=\"distance-description\">Go for <span class=\"length\">5.5 km</span>.</span>",
+                                "travelTime": 238,
+                                "length": 5538,
+                                "id": "M11",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 39.0153587,
+                                    "longitude": -77.1221781
+                                },
+                                "instruction": "Take exit <span class=\"exit\">36</span> toward <span class=\"sign\"><span lang=\"en\">Bethesda</span></span> onto <span class=\"number\">MD-187 S</span> <span class=\"next-street\">(Old Georgetown Rd)</span>. <span class=\"distance-description\">Go for <span class=\"length\">2.4 km</span>.</span>",
+                                "travelTime": 211,
+                                "length": 2365,
+                                "id": "M12",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9981818,
+                                    "longitude": -77.1093571
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Lincoln Dr</span>. <span class=\"distance-description\">Go for <span class=\"length\">506 m</span>.</span>",
+                                "travelTime": 127,
+                                "length": 506,
+                                "id": "M13",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9987397,
+                                    "longitude": -77.1037138
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">Service Rd W</span>. <span class=\"distance-description\">Go for <span class=\"length\">121 m</span>.</span>",
+                                "travelTime": 36,
+                                "length": 121,
+                                "id": "M14",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9976454,
+                                    "longitude": -77.1036172
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Service Rd S</span>. <span class=\"distance-description\">Go for <span class=\"length\">510 m</span>.</span>",
+                                "travelTime": 106,
+                                "length": 510,
+                                "id": "M15",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9999735,
+                                    "longitude": -77.100141
+                                },
+                                "instruction": "Arrive at <span class=\"street\">Service Rd S</span>. Your destination is on the left.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M16",
+                                "_type": "PrivateTransportManeuverType"
+                            }
+                        ]
+                    }
+                ],
+                "summary": {
+                    "distance": 23903,
+                    "trafficTime": 1861,
+                    "baseTime": 1803,
+                    "flags": [
+                        "noThroughRoad",
+                        "motorway",
+                        "builtUpArea",
+                        "park",
+                        "privateRoad"
+                    ],
+                    "text": "The trip takes <span class=\"length\">23.9 km</span> and <span class=\"time\">31 mins</span>.",
+                    "travelTime": 1861,
+                    "_type": "RouteSummaryType"
+                }
+            }
+        ],
+        "language": "en-us"
+    }
+}
\ No newline at end of file
diff --git a/tests/fixtures/here_travel_time/car_shortest_response.json b/tests/fixtures/here_travel_time/car_shortest_response.json
new file mode 100644
index 00000000000..765c438c1cd
--- /dev/null
+++ b/tests/fixtures/here_travel_time/car_shortest_response.json
@@ -0,0 +1,231 @@
+{
+    "response": {
+        "metaInfo": {
+            "timestamp": "2019-07-21T21:05:28Z",
+            "mapVersion": "8.30.98.154",
+            "moduleVersion": "7.2.201928-4478",
+            "interfaceVersion": "2.6.64",
+            "availableMapVersion": [
+                "8.30.98.154"
+            ]
+        },
+        "route": [
+            {
+                "waypoint": [
+                    {
+                        "linkId": "-1128310200",
+                        "mappedPosition": {
+                            "latitude": 38.9026523,
+                            "longitude": -77.048338
+                        },
+                        "originalPosition": {
+                            "latitude": 38.9029809,
+                            "longitude": -77.048338
+                        },
+                        "type": "stopOver",
+                        "spot": 0.3538462,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "K St NW",
+                        "label": "K St NW",
+                        "shapeIndex": 0,
+                        "source": "user"
+                    },
+                    {
+                        "linkId": "-18459081",
+                        "mappedPosition": {
+                            "latitude": 39.0422511,
+                            "longitude": -77.1193526
+                        },
+                        "originalPosition": {
+                            "latitude": 39.042158,
+                            "longitude": -77.119116
+                        },
+                        "type": "stopOver",
+                        "spot": 0.7253521,
+                        "sideOfStreet": "left",
+                        "mappedRoadName": "Commonwealth Dr",
+                        "label": "Commonwealth Dr",
+                        "shapeIndex": 162,
+                        "source": "user"
+                    }
+                ],
+                "mode": {
+                    "type": "shortest",
+                    "transportModes": [
+                        "car"
+                    ],
+                    "trafficMode": "enabled",
+                    "feature": []
+                },
+                "leg": [
+                    {
+                        "start": {
+                            "linkId": "-1128310200",
+                            "mappedPosition": {
+                                "latitude": 38.9026523,
+                                "longitude": -77.048338
+                            },
+                            "originalPosition": {
+                                "latitude": 38.9029809,
+                                "longitude": -77.048338
+                            },
+                            "type": "stopOver",
+                            "spot": 0.3538462,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "K St NW",
+                            "label": "K St NW",
+                            "shapeIndex": 0,
+                            "source": "user"
+                        },
+                        "end": {
+                            "linkId": "-18459081",
+                            "mappedPosition": {
+                                "latitude": 39.0422511,
+                                "longitude": -77.1193526
+                            },
+                            "originalPosition": {
+                                "latitude": 39.042158,
+                                "longitude": -77.119116
+                            },
+                            "type": "stopOver",
+                            "spot": 0.7253521,
+                            "sideOfStreet": "left",
+                            "mappedRoadName": "Commonwealth Dr",
+                            "label": "Commonwealth Dr",
+                            "shapeIndex": 162,
+                            "source": "user"
+                        },
+                        "length": 18388,
+                        "travelTime": 2493,
+                        "maneuver": [
+                            {
+                                "position": {
+                                    "latitude": 38.9026523,
+                                    "longitude": -77.048338
+                                },
+                                "instruction": "Head <span class=\"heading\">west</span> on <span class=\"street\">K St NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">79 m</span>.</span>",
+                                "travelTime": 22,
+                                "length": 79,
+                                "id": "M1",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9026523,
+                                    "longitude": -77.048825
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">22nd St NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">141 m</span>.</span>",
+                                "travelTime": 79,
+                                "length": 141,
+                                "id": "M2",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9039075,
+                                    "longitude": -77.048825
+                                },
+                                "instruction": "Keep <span class=\"direction\">left</span> onto <span class=\"next-street\">22nd St NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">841 m</span>.</span>",
+                                "travelTime": 256,
+                                "length": 841,
+                                "id": "M3",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9114928,
+                                    "longitude": -77.0487821
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Massachusetts Ave NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">145 m</span>.</span>",
+                                "travelTime": 22,
+                                "length": 145,
+                                "id": "M4",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9120293,
+                                    "longitude": -77.0502949
+                                },
+                                "instruction": "Take the <span class=\"exit\">1st exit</span> from Massachusetts Ave NW roundabout onto <span class=\"next-street\">Massachusetts Ave NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">2.8 km</span>.</span>",
+                                "travelTime": 301,
+                                "length": 2773,
+                                "id": "M5",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9286053,
+                                    "longitude": -77.073158
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">Wisconsin Ave NW</span>. <span class=\"distance-description\">Go for <span class=\"length\">3.8 km</span>.</span>",
+                                "travelTime": 610,
+                                "length": 3801,
+                                "id": "M6",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 38.9607918,
+                                    "longitude": -77.0857322
+                                },
+                                "instruction": "Continue on <span class=\"next-street\">Wisconsin Ave</span> <span class=\"number\">(MD-355)</span>. <span class=\"distance-description\">Go for <span class=\"length\">9.7 km</span>.</span>",
+                                "travelTime": 1013,
+                                "length": 9686,
+                                "id": "M7",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 39.0447664,
+                                    "longitude": -77.1116638
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Nicholson Ln</span>. <span class=\"distance-description\">Go for <span class=\"length\">559 m</span>.</span>",
+                                "travelTime": 111,
+                                "length": 559,
+                                "id": "M8",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 39.0448952,
+                                    "longitude": -77.1179724
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Commonwealth Dr</span>. <span class=\"distance-description\">Go for <span class=\"length\">341 m</span>.</span>",
+                                "travelTime": 75,
+                                "length": 341,
+                                "id": "M9",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 39.0422511,
+                                    "longitude": -77.1193526
+                                },
+                                "instruction": "Arrive at <span class=\"street\">Commonwealth Dr</span>. Your destination is on the left.",
+                                "travelTime": 4,
+                                "length": 22,
+                                "id": "M10",
+                                "_type": "PrivateTransportManeuverType"
+                            }
+                        ]
+                    }
+                ],
+                "summary": {
+                    "distance": 18388,
+                    "trafficTime": 2427,
+                    "baseTime": 2150,
+                    "flags": [
+                        "noThroughRoad",
+                        "builtUpArea",
+                        "park"
+                    ],
+                    "text": "The trip takes <span class=\"length\">18.4 km</span> and <span class=\"time\">40 mins</span>.",
+                    "travelTime": 2427,
+                    "_type": "RouteSummaryType"
+                }
+            }
+        ],
+        "language": "en-us"
+    }
+}
\ No newline at end of file
diff --git a/tests/fixtures/here_travel_time/pedestrian_response.json b/tests/fixtures/here_travel_time/pedestrian_response.json
new file mode 100644
index 00000000000..07881e8bd3d
--- /dev/null
+++ b/tests/fixtures/here_travel_time/pedestrian_response.json
@@ -0,0 +1,308 @@
+{
+    "response": {
+        "metaInfo": {
+            "timestamp": "2019-07-21T18:40:10Z",
+            "mapVersion": "8.30.98.154",
+            "moduleVersion": "7.2.201928-4478",
+            "interfaceVersion": "2.6.64",
+            "availableMapVersion": [
+                "8.30.98.154"
+            ]
+        },
+        "route": [
+            {
+                "waypoint": [
+                    {
+                        "linkId": "-1230414527",
+                        "mappedPosition": {
+                            "latitude": 41.9797859,
+                            "longitude": -87.8790879
+                        },
+                        "originalPosition": {
+                            "latitude": 41.9798,
+                            "longitude": -87.8801
+                        },
+                        "type": "stopOver",
+                        "spot": 0.5079365,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "Mannheim Rd",
+                        "label": "Mannheim Rd - US-12",
+                        "shapeIndex": 0,
+                        "source": "user"
+                    },
+                    {
+                        "linkId": "+924115108",
+                        "mappedPosition": {
+                            "latitude": 41.90413,
+                            "longitude": -87.9223502
+                        },
+                        "originalPosition": {
+                            "latitude": 41.9043,
+                            "longitude": -87.9216001
+                        },
+                        "type": "stopOver",
+                        "spot": 0.1925926,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "",
+                        "label": "",
+                        "shapeIndex": 122,
+                        "source": "user"
+                    }
+                ],
+                "mode": {
+                    "type": "fastest",
+                    "transportModes": [
+                        "pedestrian"
+                    ],
+                    "trafficMode": "disabled",
+                    "feature": []
+                },
+                "leg": [
+                    {
+                        "start": {
+                            "linkId": "-1230414527",
+                            "mappedPosition": {
+                                "latitude": 41.9797859,
+                                "longitude": -87.8790879
+                            },
+                            "originalPosition": {
+                                "latitude": 41.9798,
+                                "longitude": -87.8801
+                            },
+                            "type": "stopOver",
+                            "spot": 0.5079365,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "Mannheim Rd",
+                            "label": "Mannheim Rd - US-12",
+                            "shapeIndex": 0,
+                            "source": "user"
+                        },
+                        "end": {
+                            "linkId": "+924115108",
+                            "mappedPosition": {
+                                "latitude": 41.90413,
+                                "longitude": -87.9223502
+                            },
+                            "originalPosition": {
+                                "latitude": 41.9043,
+                                "longitude": -87.9216001
+                            },
+                            "type": "stopOver",
+                            "spot": 0.1925926,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "",
+                            "label": "",
+                            "shapeIndex": 122,
+                            "source": "user"
+                        },
+                        "length": 12533,
+                        "travelTime": 12631,
+                        "maneuver": [
+                            {
+                                "position": {
+                                    "latitude": 41.9797859,
+                                    "longitude": -87.8790879
+                                },
+                                "instruction": "Head <span class=\"heading\">south</span> on <span class=\"street\">Mannheim Rd</span>. <span class=\"distance-description\">Go for <span class=\"length\">848 m</span>.</span>",
+                                "travelTime": 848,
+                                "length": 848,
+                                "id": "M1",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9722581,
+                                    "longitude": -87.8776109
+                                },
+                                "instruction": "Take the street on the <span class=\"direction\">left</span>, <span class=\"next-street\">Mannheim Rd</span>. <span class=\"distance-description\">Go for <span class=\"length\">4.2 km</span>.</span>",
+                                "travelTime": 4239,
+                                "length": 4227,
+                                "id": "M2",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9364238,
+                                    "longitude": -87.8849387
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">W Belmont Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">595 m</span>.</span>",
+                                "travelTime": 605,
+                                "length": 595,
+                                "id": "M3",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9362521,
+                                    "longitude": -87.8921163
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">Cullerton St</span>. <span class=\"distance-description\">Go for <span class=\"length\">406 m</span>.</span>",
+                                "travelTime": 411,
+                                "length": 406,
+                                "id": "M4",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9326043,
+                                    "longitude": -87.8919983
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">Cullerton St</span>. <span class=\"distance-description\">Go for <span class=\"length\">1.2 km</span>.</span>",
+                                "travelTime": 1249,
+                                "length": 1239,
+                                "id": "M5",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9217896,
+                                    "longitude": -87.8928781
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">E Fullerton Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">786 m</span>.</span>",
+                                "travelTime": 796,
+                                "length": 786,
+                                "id": "M6",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9216394,
+                                    "longitude": -87.9023838
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">La Porte Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">424 m</span>.</span>",
+                                "travelTime": 430,
+                                "length": 424,
+                                "id": "M7",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9180024,
+                                    "longitude": -87.9028559
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">E Palmer Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">864 m</span>.</span>",
+                                "travelTime": 875,
+                                "length": 864,
+                                "id": "M8",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9175196,
+                                    "longitude": -87.9132199
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">N Railroad Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">1.2 km</span>.</span>",
+                                "travelTime": 1180,
+                                "length": 1170,
+                                "id": "M9",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9070268,
+                                    "longitude": -87.9130161
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">W North Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">638 m</span>.</span>",
+                                "travelTime": 638,
+                                "length": 638,
+                                "id": "M10",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9068551,
+                                    "longitude": -87.9207087
+                                },
+                                "instruction": "Take the street on the <span class=\"direction\">left</span>, <span class=\"next-street\">E North Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">354 m</span>.</span>",
+                                "travelTime": 354,
+                                "length": 354,
+                                "id": "M11",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9065869,
+                                    "longitude": -87.9249573
+                                },
+                                "instruction": "Take the street on the <span class=\"direction\">left</span>, <span class=\"next-street\">E North Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">228 m</span>.</span>",
+                                "travelTime": 242,
+                                "length": 228,
+                                "id": "M12",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9065225,
+                                    "longitude": -87.9277039
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span>. <span class=\"distance-description\">Go for <span class=\"length\">409 m</span>.</span>",
+                                "travelTime": 419,
+                                "length": 409,
+                                "id": "M13",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9040334,
+                                    "longitude": -87.9260409
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">E Third St</span>. <span class=\"distance-description\">Go for <span class=\"length\">206 m</span>.</span>",
+                                "travelTime": 206,
+                                "length": 206,
+                                "id": "M14",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9038832,
+                                    "longitude": -87.9236054
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span>. <span class=\"distance-description\">Go for <span class=\"length\">113 m</span>.</span>",
+                                "travelTime": 113,
+                                "length": 113,
+                                "id": "M15",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9039047,
+                                    "longitude": -87.9222536
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span>. <span class=\"distance-description\">Go for <span class=\"length\">26 m</span>.</span>",
+                                "travelTime": 26,
+                                "length": 26,
+                                "id": "M16",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.90413,
+                                    "longitude": -87.9223502
+                                },
+                                "instruction": "Arrive at your destination on the right.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M17",
+                                "_type": "PrivateTransportManeuverType"
+                            }
+                        ]
+                    }
+                ],
+                "summary": {
+                    "distance": 12533,
+                    "baseTime": 12631,
+                    "flags": [
+                        "noThroughRoad",
+                        "builtUpArea",
+                        "park",
+                        "privateRoad"
+                    ],
+                    "text": "The trip takes <span class=\"length\">12.5 km</span> and <span class=\"time\">3:31 h</span>.",
+                    "travelTime": 12631,
+                    "_type": "RouteSummaryType"
+                }
+            }
+        ],
+        "language": "en-us"
+    }
+}
\ No newline at end of file
diff --git a/tests/fixtures/here_travel_time/public_response.json b/tests/fixtures/here_travel_time/public_response.json
new file mode 100644
index 00000000000..149b4d06c39
--- /dev/null
+++ b/tests/fixtures/here_travel_time/public_response.json
@@ -0,0 +1,294 @@
+{
+    "response": {
+        "metaInfo": {
+            "timestamp": "2019-07-21T18:40:37Z",
+            "mapVersion": "8.30.98.154",
+            "moduleVersion": "7.2.201928-4478",
+            "interfaceVersion": "2.6.64",
+            "availableMapVersion": [
+                "8.30.98.154"
+            ]
+        },
+        "route": [
+            {
+                "waypoint": [
+                    {
+                        "linkId": "-1230414527",
+                        "mappedPosition": {
+                            "latitude": 41.9797859,
+                            "longitude": -87.8790879
+                        },
+                        "originalPosition": {
+                            "latitude": 41.9798,
+                            "longitude": -87.8801
+                        },
+                        "type": "stopOver",
+                        "spot": 0.5079365,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "Mannheim Rd",
+                        "label": "Mannheim Rd - US-12",
+                        "shapeIndex": 0,
+                        "source": "user"
+                    },
+                    {
+                        "linkId": "+924115108",
+                        "mappedPosition": {
+                            "latitude": 41.90413,
+                            "longitude": -87.9223502
+                        },
+                        "originalPosition": {
+                            "latitude": 41.9043,
+                            "longitude": -87.9216001
+                        },
+                        "type": "stopOver",
+                        "spot": 0.1925926,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "",
+                        "label": "",
+                        "shapeIndex": 191,
+                        "source": "user"
+                    }
+                ],
+                "mode": {
+                    "type": "fastest",
+                    "transportModes": [
+                        "publicTransport"
+                    ],
+                    "trafficMode": "disabled",
+                    "feature": []
+                },
+                "leg": [
+                    {
+                        "start": {
+                            "linkId": "-1230414527",
+                            "mappedPosition": {
+                                "latitude": 41.9797859,
+                                "longitude": -87.8790879
+                            },
+                            "originalPosition": {
+                                "latitude": 41.9798,
+                                "longitude": -87.8801
+                            },
+                            "type": "stopOver",
+                            "spot": 0.5079365,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "Mannheim Rd",
+                            "label": "Mannheim Rd - US-12",
+                            "shapeIndex": 0,
+                            "source": "user"
+                        },
+                        "end": {
+                            "linkId": "+924115108",
+                            "mappedPosition": {
+                                "latitude": 41.90413,
+                                "longitude": -87.9223502
+                            },
+                            "originalPosition": {
+                                "latitude": 41.9043,
+                                "longitude": -87.9216001
+                            },
+                            "type": "stopOver",
+                            "spot": 0.1925926,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "",
+                            "label": "",
+                            "shapeIndex": 191,
+                            "source": "user"
+                        },
+                        "length": 22325,
+                        "travelTime": 5350,
+                        "maneuver": [
+                            {
+                                "position": {
+                                    "latitude": 41.9797859,
+                                    "longitude": -87.8790879
+                                },
+                                "instruction": "Head <span class=\"heading\">south</span> on <span class=\"street\">Mannheim Rd</span>. <span class=\"distance-description\">Go for <span class=\"length\">848 m</span>.</span>",
+                                "travelTime": 848,
+                                "length": 848,
+                                "id": "M1",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9722581,
+                                    "longitude": -87.8776109
+                                },
+                                "instruction": "Take the street on the <span class=\"direction\">left</span>, <span class=\"next-street\">Mannheim Rd</span>. <span class=\"distance-description\">Go for <span class=\"length\">825 m</span>.</span>",
+                                "travelTime": 825,
+                                "length": 825,
+                                "id": "M2",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9650483,
+                                    "longitude": -87.8769565
+                                },
+                                "instruction": "Go to the stop <span class=\"station\">Mannheim/Lawrence</span> and take the <span class=\"transit\">bus</span> <span class=\"line\">332</span> toward <span class=\"destination\">Palmer/Schiller</span>. <span class=\"distance-description\">Follow for <span class=\"stops\">7 stops</span>.</span>",
+                                "travelTime": 475,
+                                "length": 4360,
+                                "id": "M3",
+                                "stopName": "Mannheim/Lawrence",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9541478,
+                                    "longitude": -87.9133594
+                                },
+                                "instruction": "Get off at <span class=\"station\">Irving Park/Taft</span>.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M4",
+                                "stopName": "Irving Park/Taft",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9541478,
+                                    "longitude": -87.9133594
+                                },
+                                "instruction": "Take the <span class=\"transit\">bus</span> <span class=\"line\">332</span> toward <span class=\"destination\">Cargo Rd./Delta Cargo</span>. <span class=\"distance-description\">Follow for <span class=\"stops\">1 stop</span>.</span>",
+                                "travelTime": 155,
+                                "length": 3505,
+                                "id": "M5",
+                                "stopName": "Irving Park/Taft",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9599199,
+                                    "longitude": -87.9162776
+                                },
+                                "instruction": "Get off at <span class=\"station\">Cargo Rd./S. Access Rd./Lufthansa</span>.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M6",
+                                "stopName": "Cargo Rd./S. Access Rd./Lufthansa",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9599199,
+                                    "longitude": -87.9162776
+                                },
+                                "instruction": "Take the <span class=\"transit\">bus</span> <span class=\"line\">332</span> toward <span class=\"destination\">Palmer/Schiller</span>. <span class=\"distance-description\">Follow for <span class=\"stops\">41 stops</span>.</span>",
+                                "travelTime": 1510,
+                                "length": 11261,
+                                "id": "M7",
+                                "stopName": "Cargo Rd./S. Access Rd./Lufthansa",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9041729,
+                                    "longitude": -87.9399669
+                                },
+                                "instruction": "Get off at <span class=\"station\">York/Third</span>.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M8",
+                                "stopName": "York/Third",
+                                "nextRoadName": "N York St",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9041729,
+                                    "longitude": -87.9399669
+                                },
+                                "instruction": "Head <span class=\"heading\">east</span> on <span class=\"street\">N York St</span>. <span class=\"distance-description\">Go for <span class=\"length\">33 m</span>.</span>",
+                                "travelTime": 43,
+                                "length": 33,
+                                "id": "M9",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9039476,
+                                    "longitude": -87.9398811
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">E Third St</span>. <span class=\"distance-description\">Go for <span class=\"length\">1.4 km</span>.</span>",
+                                "travelTime": 1355,
+                                "length": 1354,
+                                "id": "M10",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9038832,
+                                    "longitude": -87.9236054
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span>. <span class=\"distance-description\">Go for <span class=\"length\">113 m</span>.</span>",
+                                "travelTime": 113,
+                                "length": 113,
+                                "id": "M11",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9039047,
+                                    "longitude": -87.9222536
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span>. <span class=\"distance-description\">Go for <span class=\"length\">26 m</span>.</span>",
+                                "travelTime": 26,
+                                "length": 26,
+                                "id": "M12",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.90413,
+                                    "longitude": -87.9223502
+                                },
+                                "instruction": "Arrive at your destination on the right.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M13",
+                                "_type": "PrivateTransportManeuverType"
+                            }
+                        ]
+                    }
+                ],
+                "publicTransportLine": [
+                    {
+                        "lineName": "332",
+                        "companyName": "",
+                        "destination": "Palmer/Schiller",
+                        "type": "busPublic",
+                        "id": "L1"
+                    },
+                    {
+                        "lineName": "332",
+                        "companyName": "",
+                        "destination": "Cargo Rd./Delta Cargo",
+                        "type": "busPublic",
+                        "id": "L2"
+                    },
+                    {
+                        "lineName": "332",
+                        "companyName": "",
+                        "destination": "Palmer/Schiller",
+                        "type": "busPublic",
+                        "id": "L3"
+                    }
+                ],
+                "summary": {
+                    "distance": 22325,
+                    "baseTime": 5350,
+                    "flags": [
+                        "noThroughRoad",
+                        "builtUpArea",
+                        "park"
+                    ],
+                    "text": "The trip takes <span class=\"length\">22.3 km</span> and <span class=\"time\">1:29 h</span>.",
+                    "travelTime": 5350,
+                    "departure": "1970-01-01T00:00:00Z",
+                    "_type": "PublicTransportRouteSummaryType"
+                }
+            }
+        ],
+        "language": "en-us"
+    }
+}
\ No newline at end of file
diff --git a/tests/fixtures/here_travel_time/public_time_table_response.json b/tests/fixtures/here_travel_time/public_time_table_response.json
new file mode 100644
index 00000000000..52df0d4eb35
--- /dev/null
+++ b/tests/fixtures/here_travel_time/public_time_table_response.json
@@ -0,0 +1,308 @@
+{
+    "response": {
+        "metaInfo": {
+            "timestamp": "2019-08-06T06:43:24Z",
+            "mapVersion": "8.30.99.152",
+            "moduleVersion": "7.2.201931-4739",
+            "interfaceVersion": "2.6.66",
+            "availableMapVersion": [
+                "8.30.99.152"
+            ]
+        },
+        "route": [
+            {
+                "waypoint": [
+                    {
+                        "linkId": "-1230414527",
+                        "mappedPosition": {
+                            "latitude": 41.9797859,
+                            "longitude": -87.8790879
+                        },
+                        "originalPosition": {
+                            "latitude": 41.9798,
+                            "longitude": -87.8801
+                        },
+                        "type": "stopOver",
+                        "spot": 0.5079365,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "Mannheim Rd",
+                        "label": "Mannheim Rd - US-12",
+                        "shapeIndex": 0,
+                        "source": "user"
+                    },
+                    {
+                        "linkId": "+924115108",
+                        "mappedPosition": {
+                            "latitude": 41.90413,
+                            "longitude": -87.9223502
+                        },
+                        "originalPosition": {
+                            "latitude": 41.9043,
+                            "longitude": -87.9216001
+                        },
+                        "type": "stopOver",
+                        "spot": 0.1925926,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "",
+                        "label": "",
+                        "shapeIndex": 111,
+                        "source": "user"
+                    }
+                ],
+                "mode": {
+                    "type": "fastest",
+                    "transportModes": [
+                        "publicTransportTimeTable"
+                    ],
+                    "trafficMode": "disabled",
+                    "feature": []
+                },
+                "leg": [
+                    {
+                        "start": {
+                            "linkId": "-1230414527",
+                            "mappedPosition": {
+                                "latitude": 41.9797859,
+                                "longitude": -87.8790879
+                            },
+                            "originalPosition": {
+                                "latitude": 41.9798,
+                                "longitude": -87.8801
+                            },
+                            "type": "stopOver",
+                            "spot": 0.5079365,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "Mannheim Rd",
+                            "label": "Mannheim Rd - US-12",
+                            "shapeIndex": 0,
+                            "source": "user"
+                        },
+                        "end": {
+                            "linkId": "+924115108",
+                            "mappedPosition": {
+                                "latitude": 41.90413,
+                                "longitude": -87.9223502
+                            },
+                            "originalPosition": {
+                                "latitude": 41.9043,
+                                "longitude": -87.9216001
+                            },
+                            "type": "stopOver",
+                            "spot": 0.1925926,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "",
+                            "label": "",
+                            "shapeIndex": 111,
+                            "source": "user"
+                        },
+                        "length": 14775,
+                        "travelTime": 4784,
+                        "maneuver": [
+                            {
+                                "position": {
+                                    "latitude": 41.9797859,
+                                    "longitude": -87.8790879
+                                },
+                                "instruction": "Head <span class=\"heading\">south</span> on <span class=\"street\">Mannheim Rd</span>. <span class=\"distance-description\">Go for <span class=\"length\">848 m</span>.</span>",
+                                "travelTime": 848,
+                                "length": 848,
+                                "id": "M1",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9722581,
+                                    "longitude": -87.8776109
+                                },
+                                "instruction": "Take the street on the <span class=\"direction\">left</span>, <span class=\"next-street\">Mannheim Rd</span>. <span class=\"distance-description\">Go for <span class=\"length\">812 m</span>.</span>",
+                                "travelTime": 812,
+                                "length": 812,
+                                "id": "M2",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.965051,
+                                    "longitude": -87.8769591
+                                },
+                                "instruction": "Go to the <span class=\"company\">Bus</span> stop <span class=\"station\">Mannheim/Lawrence</span> and take the <span class=\"transit\">bus</span> <span class=\"line\">330</span> toward <span class=\"destination\">Archer/Harlem (Terminal)</span>. <span class=\"distance-description\">Follow for <span class=\"stops\">33 stops</span>.</span>",
+                                "travelTime": 900,
+                                "length": 7815,
+                                "id": "M3",
+                                "stopName": "Mannheim/Lawrence",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.896836,
+                                    "longitude": -87.883771
+                                },
+                                "instruction": "Get off at <span class=\"station\">Mannheim/Lake</span>.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M4",
+                                "stopName": "Mannheim/Lake",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.896836,
+                                    "longitude": -87.883771
+                                },
+                                "instruction": "Walk to <span class=\"company\">Bus</span> <span class=\"station\">Lake/Mannheim</span>.",
+                                "travelTime": 300,
+                                "length": 72,
+                                "id": "M5",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.897263,
+                                    "longitude": -87.8842648
+                                },
+                                "instruction": "Take the <span class=\"transit\">bus</span> <span class=\"line\">309</span> toward <span class=\"destination\">Elmhurst Metra Station</span>. <span class=\"distance-description\">Follow for <span class=\"stops\">18 stops</span>.</span>",
+                                "travelTime": 1020,
+                                "length": 4362,
+                                "id": "M6",
+                                "stopName": "Lake/Mannheim",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9066347,
+                                    "longitude": -87.928671
+                                },
+                                "instruction": "Get off at <span class=\"station\">North/Berteau</span>.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M7",
+                                "stopName": "North/Berteau",
+                                "nextRoadName": "E Berteau Ave",
+                                "_type": "PublicTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9066347,
+                                    "longitude": -87.928671
+                                },
+                                "instruction": "Head <span class=\"heading\">north</span> on <span class=\"street\">E Berteau Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">23 m</span>.</span>",
+                                "travelTime": 40,
+                                "length": 23,
+                                "id": "M8",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9067693,
+                                    "longitude": -87.9284549
+                                },
+                                "instruction": "Turn <span class=\"direction\">right</span> onto <span class=\"next-street\">E Berteau Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">40 m</span>.</span>",
+                                "travelTime": 44,
+                                "length": 40,
+                                "id": "M9",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9065011,
+                                    "longitude": -87.9282939
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">E North Ave</span>. <span class=\"distance-description\">Go for <span class=\"length\">49 m</span>.</span>",
+                                "travelTime": 56,
+                                "length": 49,
+                                "id": "M10",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9065225,
+                                    "longitude": -87.9277039
+                                },
+                                "instruction": "Turn <span class=\"direction\">slightly right</span>. <span class=\"distance-description\">Go for <span class=\"length\">409 m</span>.</span>",
+                                "travelTime": 419,
+                                "length": 409,
+                                "id": "M11",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9040334,
+                                    "longitude": -87.9260409
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span> onto <span class=\"next-street\">E Third St</span>. <span class=\"distance-description\">Go for <span class=\"length\">206 m</span>.</span>",
+                                "travelTime": 206,
+                                "length": 206,
+                                "id": "M12",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9038832,
+                                    "longitude": -87.9236054
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span>. <span class=\"distance-description\">Go for <span class=\"length\">113 m</span>.</span>",
+                                "travelTime": 113,
+                                "length": 113,
+                                "id": "M13",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9039047,
+                                    "longitude": -87.9222536
+                                },
+                                "instruction": "Turn <span class=\"direction\">left</span>. <span class=\"distance-description\">Go for <span class=\"length\">26 m</span>.</span>",
+                                "travelTime": 26,
+                                "length": 26,
+                                "id": "M14",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.90413,
+                                    "longitude": -87.9223502
+                                },
+                                "instruction": "Arrive at your destination on the right.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M15",
+                                "_type": "PrivateTransportManeuverType"
+                            }
+                        ]
+                    }
+                ],
+                "publicTransportLine": [
+                    {
+                        "lineName": "330",
+                        "companyName": "PACE",
+                        "destination": "Archer/Harlem (Terminal)",
+                        "type": "busPublic",
+                        "id": "L1"
+                    },
+                    {
+                        "lineName": "309",
+                        "companyName": "PACE",
+                        "destination": "Elmhurst Metra Station",
+                        "type": "busPublic",
+                        "id": "L2"
+                    }
+                ],
+                "summary": {
+                    "distance": 14775,
+                    "baseTime": 4784,
+                    "flags": [
+                        "noThroughRoad",
+                        "builtUpArea",
+                        "park"
+                    ],
+                    "text": "The trip takes <span class=\"length\">14.8 km</span> and <span class=\"time\">1:20 h</span>.",
+                    "travelTime": 4784,
+                    "departure": "2019-08-06T05:09:20-05:00",
+                    "timetableExpiration": "2019-08-04T00:00:00Z",
+                    "_type": "PublicTransportRouteSummaryType"
+                }
+            }
+        ],
+        "language": "en-us"
+    }
+}
\ No newline at end of file
diff --git a/tests/fixtures/here_travel_time/routing_error_invalid_credentials.json b/tests/fixtures/here_travel_time/routing_error_invalid_credentials.json
new file mode 100644
index 00000000000..81fb246178c
--- /dev/null
+++ b/tests/fixtures/here_travel_time/routing_error_invalid_credentials.json
@@ -0,0 +1,15 @@
+{
+    "_type": "ns2:RoutingServiceErrorType",
+    "type": "PermissionError",
+    "subtype": "InvalidCredentials",
+    "details": "This is not a valid app_id and app_code pair. Please verify that the values are not swapped between the app_id and app_code and the values provisioned by HERE (either by your customer representative or via http://developer.here.com/myapps) were copied correctly into the request.",
+    "metaInfo": {
+        "timestamp": "2019-07-10T09:43:14Z",
+        "mapVersion": "8.30.98.152",
+        "moduleVersion": "7.2.201927-4307",
+        "interfaceVersion": "2.6.64",
+        "availableMapVersion": [
+            "8.30.98.152"
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/fixtures/here_travel_time/routing_error_no_route_found.json b/tests/fixtures/here_travel_time/routing_error_no_route_found.json
new file mode 100644
index 00000000000..a776fa91c43
--- /dev/null
+++ b/tests/fixtures/here_travel_time/routing_error_no_route_found.json
@@ -0,0 +1,21 @@
+{
+    "_type": "ns2:RoutingServiceErrorType",
+    "type": "ApplicationError",
+    "subtype": "NoRouteFound",
+    "details": "Error is NGEO_ERROR_ROUTE_NO_END_POINT",
+    "additionalData": [
+        {
+            "key": "error_code",
+            "value": "NGEO_ERROR_ROUTE_NO_END_POINT"
+        }
+    ],
+    "metaInfo": {
+        "timestamp": "2019-07-10T09:51:04Z",
+        "mapVersion": "8.30.98.152",
+        "moduleVersion": "7.2.201927-4307",
+        "interfaceVersion": "2.6.64",
+        "availableMapVersion": [
+            "8.30.98.152"
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/fixtures/here_travel_time/truck_response.json b/tests/fixtures/here_travel_time/truck_response.json
new file mode 100644
index 00000000000..a302d564902
--- /dev/null
+++ b/tests/fixtures/here_travel_time/truck_response.json
@@ -0,0 +1,187 @@
+{
+    "response": {
+        "metaInfo": {
+            "timestamp": "2019-07-21T14:25:00Z",
+            "mapVersion": "8.30.98.154",
+            "moduleVersion": "7.2.201928-4478",
+            "interfaceVersion": "2.6.64",
+            "availableMapVersion": [
+                "8.30.98.154"
+            ]
+        },
+        "route": [
+            {
+                "waypoint": [
+                    {
+                        "linkId": "+930461269",
+                        "mappedPosition": {
+                            "latitude": 41.9800687,
+                            "longitude": -87.8805614
+                        },
+                        "originalPosition": {
+                            "latitude": 41.9798,
+                            "longitude": -87.8801
+                        },
+                        "type": "stopOver",
+                        "spot": 0.5555556,
+                        "sideOfStreet": "right",
+                        "mappedRoadName": "",
+                        "label": "",
+                        "shapeIndex": 0,
+                        "source": "user"
+                    },
+                    {
+                        "linkId": "-1035319462",
+                        "mappedPosition": {
+                            "latitude": 41.9042909,
+                            "longitude": -87.9216528
+                        },
+                        "originalPosition": {
+                            "latitude": 41.9043,
+                            "longitude": -87.9216001
+                        },
+                        "type": "stopOver",
+                        "spot": 0,
+                        "sideOfStreet": "left",
+                        "mappedRoadName": "Eisenhower Expy E",
+                        "label": "Eisenhower Expy E - I-290",
+                        "shapeIndex": 135,
+                        "source": "user"
+                    }
+                ],
+                "mode": {
+                    "type": "fastest",
+                    "transportModes": [
+                        "truck"
+                    ],
+                    "trafficMode": "disabled",
+                    "feature": []
+                },
+                "leg": [
+                    {
+                        "start": {
+                            "linkId": "+930461269",
+                            "mappedPosition": {
+                                "latitude": 41.9800687,
+                                "longitude": -87.8805614
+                            },
+                            "originalPosition": {
+                                "latitude": 41.9798,
+                                "longitude": -87.8801
+                            },
+                            "type": "stopOver",
+                            "spot": 0.5555556,
+                            "sideOfStreet": "right",
+                            "mappedRoadName": "",
+                            "label": "",
+                            "shapeIndex": 0,
+                            "source": "user"
+                        },
+                        "end": {
+                            "linkId": "-1035319462",
+                            "mappedPosition": {
+                                "latitude": 41.9042909,
+                                "longitude": -87.9216528
+                            },
+                            "originalPosition": {
+                                "latitude": 41.9043,
+                                "longitude": -87.9216001
+                            },
+                            "type": "stopOver",
+                            "spot": 0,
+                            "sideOfStreet": "left",
+                            "mappedRoadName": "Eisenhower Expy E",
+                            "label": "Eisenhower Expy E - I-290",
+                            "shapeIndex": 135,
+                            "source": "user"
+                        },
+                        "length": 13049,
+                        "travelTime": 812,
+                        "maneuver": [
+                            {
+                                "position": {
+                                    "latitude": 41.9800687,
+                                    "longitude": -87.8805614
+                                },
+                                "instruction": "Take ramp onto <span class=\"number\">I-190</span>. <span class=\"distance-description\">Go for <span class=\"length\">631 m</span>.</span>",
+                                "travelTime": 53,
+                                "length": 631,
+                                "id": "M1",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.98259,
+                                    "longitude": -87.8744352
+                                },
+                                "instruction": "Take exit <span class=\"exit\">1D</span> toward <span class=\"sign\"><span lang=\"en\">Indiana</span></span> onto <span class=\"number\">I-294 S</span> <span class=\"next-street\">(Tri-State Tollway)</span>. <span class=\"distance-description\">Go for <span class=\"length\">10.9 km</span>.</span>",
+                                "travelTime": 573,
+                                "length": 10872,
+                                "id": "M2",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9059324,
+                                    "longitude": -87.9199362
+                                },
+                                "instruction": "Take exit <span class=\"exit\">33</span> toward <span class=\"sign\"><span lang=\"en\">Rockford</span>/<span lang=\"en\">US-20</span>/<span lang=\"en\">IL-64</span></span> onto <span class=\"number\">I-290 W</span> <span class=\"next-street\">(Eisenhower Expy W)</span>. <span class=\"distance-description\">Go for <span class=\"length\">475 m</span>.</span>",
+                                "travelTime": 54,
+                                "length": 475,
+                                "id": "M3",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9067156,
+                                    "longitude": -87.9237771
+                                },
+                                "instruction": "Take exit <span class=\"exit\">13B</span> toward <span class=\"sign\"><span lang=\"en\">North Ave</span></span> onto <span class=\"number\">IL-64 W</span> <span class=\"next-street\">(E North Ave)</span>. <span class=\"distance-description\">Go for <span class=\"length\">435 m</span>.</span>",
+                                "travelTime": 51,
+                                "length": 435,
+                                "id": "M4",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9065869,
+                                    "longitude": -87.9249573
+                                },
+                                "instruction": "Take ramp onto <span class=\"number\">I-290 E</span> <span class=\"next-street\">(Eisenhower Expy E)</span> toward <span class=\"sign\"><span lang=\"en\">Chicago</span>/<span lang=\"en\">I-294 S</span></span>. <span class=\"distance-description\">Go for <span class=\"length\">636 m</span>.</span>",
+                                "travelTime": 81,
+                                "length": 636,
+                                "id": "M5",
+                                "_type": "PrivateTransportManeuverType"
+                            },
+                            {
+                                "position": {
+                                    "latitude": 41.9042909,
+                                    "longitude": -87.9216528
+                                },
+                                "instruction": "Arrive at <span class=\"street\">Eisenhower Expy E</span> <span class=\"number\">(I-290)</span>. Your destination is on the left.",
+                                "travelTime": 0,
+                                "length": 0,
+                                "id": "M6",
+                                "_type": "PrivateTransportManeuverType"
+                            }
+                        ]
+                    }
+                ],
+                "summary": {
+                    "distance": 13049,
+                    "trafficTime": 812,
+                    "baseTime": 812,
+                    "flags": [
+                        "tollroad",
+                        "motorway",
+                        "builtUpArea"
+                    ],
+                    "text": "The trip takes <span class=\"length\">13.0 km</span> and <span class=\"time\">14 mins</span>.",
+                    "travelTime": 812,
+                    "_type": "RouteSummaryType"
+                }
+            }
+        ],
+        "language": "en-us"
+    }
+}
\ No newline at end of file
-- 
GitLab