From 781a4267cfb16c0e92b5b8a299c6428d9a3f269b Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Wed, 25 Jan 2023 10:33:43 +0100 Subject: [PATCH] Calculate data using all sections for here_travel_time (#86576) Calculate data using all sections --- .../here_travel_time/coordinator.py | 44 ++-- tests/components/here_travel_time/conftest.py | 13 ++ .../fixtures/bike_response.json | 214 ++++++++++++++++++ .../here_travel_time/test_sensor.py | 51 +++++ 4 files changed, 309 insertions(+), 13 deletions(-) create mode 100644 tests/components/here_travel_time/fixtures/bike_response.json diff --git a/homeassistant/components/here_travel_time/coordinator.py b/homeassistant/components/here_travel_time/coordinator.py index e8eee4054ec..e76691253f9 100644 --- a/homeassistant/components/here_travel_time/coordinator.py +++ b/homeassistant/components/here_travel_time/coordinator.py @@ -117,25 +117,43 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator[HERETravelTimeData] def _parse_routing_response(self, response: dict[str, Any]) -> HERETravelTimeData: """Parse the routing response dict to a HERETravelTimeData.""" - section: dict[str, Any] = response["routes"][0]["sections"][0] - summary: dict[str, int] = section["summary"] - mapped_origin_lat: float = section["departure"]["place"]["location"]["lat"] - mapped_origin_lon: float = section["departure"]["place"]["location"]["lng"] - mapped_destination_lat: float = section["arrival"]["place"]["location"]["lat"] - mapped_destination_lon: float = section["arrival"]["place"]["location"]["lng"] - distance: float = DistanceConverter.convert( - summary["length"], UnitOfLength.METERS, UnitOfLength.KILOMETERS - ) + distance: float = 0.0 + duration: float = 0.0 + duration_in_traffic: float = 0.0 + + for section in response["routes"][0]["sections"]: + distance += DistanceConverter.convert( + section["summary"]["length"], + UnitOfLength.METERS, + UnitOfLength.KILOMETERS, + ) + duration += section["summary"]["baseDuration"] + duration_in_traffic += section["summary"]["duration"] + + first_section = response["routes"][0]["sections"][0] + last_section = response["routes"][0]["sections"][-1] + mapped_origin_lat: float = first_section["departure"]["place"]["location"][ + "lat" + ] + mapped_origin_lon: float = first_section["departure"]["place"]["location"][ + "lng" + ] + mapped_destination_lat: float = last_section["arrival"]["place"]["location"][ + "lat" + ] + mapped_destination_lon: float = last_section["arrival"]["place"]["location"][ + "lng" + ] origin_name: str | None = None - if (names := section["spans"][0].get("names")) is not None: + if (names := first_section["spans"][0].get("names")) is not None: origin_name = names[0]["value"] destination_name: str | None = None - if (names := section["spans"][-1].get("names")) is not None: + if (names := last_section["spans"][-1].get("names")) is not None: destination_name = names[0]["value"] return HERETravelTimeData( attribution=None, - duration=round(summary["baseDuration"] / 60), - duration_in_traffic=round(summary["duration"] / 60), + duration=round(duration / 60), + duration_in_traffic=round(duration_in_traffic / 60), distance=distance, origin=f"{mapped_origin_lat},{mapped_origin_lon}", destination=f"{mapped_destination_lat},{mapped_destination_lon}", diff --git a/tests/components/here_travel_time/conftest.py b/tests/components/here_travel_time/conftest.py index 8069583df76..dff91a4e1fb 100644 --- a/tests/components/here_travel_time/conftest.py +++ b/tests/components/here_travel_time/conftest.py @@ -13,6 +13,7 @@ TRANSIT_RESPONSE = json.loads( NO_ATTRIBUTION_TRANSIT_RESPONSE = json.loads( load_fixture("here_travel_time/no_attribution_transit_route_response.json") ) +BIKE_RESPONSE = json.loads(load_fixture("here_travel_time/bike_response.json")) @pytest.fixture(name="valid_response") @@ -27,6 +28,18 @@ def valid_response_fixture(): yield mock +@pytest.fixture(name="bike_response") +def bike_response_fixture(): + """Return valid api response.""" + with patch( + "here_transit.HERETransitApi.route", return_value=TRANSIT_RESPONSE + ), patch( + "here_routing.HERERoutingApi.route", + return_value=BIKE_RESPONSE, + ) as mock: + yield mock + + @pytest.fixture(name="no_attribution_response") def no_attribution_response_fixture(): """Return valid api response without attribution.""" diff --git a/tests/components/here_travel_time/fixtures/bike_response.json b/tests/components/here_travel_time/fixtures/bike_response.json new file mode 100644 index 00000000000..4dc9ccab03c --- /dev/null +++ b/tests/components/here_travel_time/fixtures/bike_response.json @@ -0,0 +1,214 @@ +{ + "routes": [ + { + "id": "6d8ae729-3b30-4d81-adaf-6a485b15b70a", + "sections": [ + { + "id": "c8d12b37-05e1-47f0-a5c2-43f5fb589768", + "type": "pedestrian", + "departure": { + "time": "2023-01-23T18:26:12+01:00", + "place": { + "type": "place", + "location": { + "lat": 49.1260894, + "lng": 6.1843356 + }, + "originalLocation": { + "lat": 49.1264093, + "lng": 6.1841419 + } + } + }, + "arrival": { + "time": "2023-01-23T18:29:09+01:00", + "place": { + "type": "place", + "location": { + "lat": 49.12547, + "lng": 6.18242 + } + } + }, + "summary": { + "duration": 177, + "length": 157, + "baseDuration": 177 + }, + "polyline": "BGyst29Cg5u5LpOj2BjIzZnQ_nB", + "spans": [ + { + "offset": 0, + "names": [ + { + "value": "Chemin de Halage", + "language": "fr" + } + ] + } + ], + "transport": { + "mode": "pedestrian" + } + }, + { + "id": "d37123f4-034a-4c46-8678-596f999e8eae", + "type": "vehicle", + "departure": { + "time": "2023-01-23T18:29:09+01:00", + "place": { + "type": "place", + "location": { + "lat": 49.12547, + "lng": 6.18242 + } + } + }, + "arrival": { + "time": "2023-01-23T18:44:41+01:00", + "place": { + "type": "place", + "location": { + "lat": 49.1025668, + "lng": 6.1768518 + }, + "originalLocation": { + "lat": 49.1025784, + "lng": 6.1770297 + } + } + }, + "summary": { + "duration": 932, + "length": 3426, + "baseDuration": 932 + }, + "polyline": "BG8ls29Cohr5L0UjXgFvH4DnGgFrJ7G3IvHrJrJvMnB7B_JjN_JzPvH_JjDsJjD8G7B4DrEkIvHwMzF4I7GgKjDgF7BwC7G0KjN0U3I4D7GsEvHkDzK4D7a4I3IwCzesJvH8B7GoBjDU7LsEjIwC_JkDrJkD_JsEvH4DvH4DvHsE3IgFrEkD_E4DvH0F7G0FrJ4IzFsErE4DjDgFnGsE_EwCrEoB3DUzFUnGAvHT7GTjDTvHT7GU7BArJUjD4DvCwCzFgF_EsE_EgF_JoLzF0F_EsEzFsErEkD3DwC3D8B3D8BrE8B3DoBvH8BjI8BzFoBzFUjIUnGAjSAzFTnLvC_E7BzF7B_E7B_EjD7G3D7G7B_ETnpBvCnQTzUnB3DAvMT7B7BnGvCzFrErEvCzFjDzF3DnGzF_EzF3I7LnV3cnG3IzF0K7LsTrE8G3I4NvC4DvC4D7BwC7BwCvCwCjD4DvCkDjD4D3D4DjDkDzFsEvCA3DUjDUrEUrEUrEU_ETrEvCnGrErd_nB3DnGnLnQ_JzP7Q_Y7G_JvR7ajI7Lrd3rBvH7LzZrnBnL3SnLjSjI7Lna7pBjN_T_EjIvCrE7GnL7GjNrE3IjDrEjDrEvCjDzF3IvCrE_JzU3Nrd_J_TvCrEvC3DT7BnBvC7B3D3XvvB7kBjuCoB7BoB7BUnBU7BU7BU3DAvCTjDT7BnBjDnBvC7BvCnBnB7BT7BTnBA7BU7BoBnBoBnB8BnB8BnBkDTwC_vC0PzoBwMvb8GnB0jB7B4cUgP4D0oB7foGZE", + "spans": [ + { + "offset": 0 + }, + { + "offset": 4, + "names": [ + { + "value": "Avenue de Blida", + "language": "fr" + } + ] + }, + { + "offset": 11, + "names": [ + { + "value": "Pont des Grilles", + "language": "fr" + } + ] + }, + { + "offset": 22, + "names": [ + { + "value": "Boulevard Paixhans", + "language": "fr" + } + ] + }, + { + "offset": 49, + "names": [ + { + "value": "Boulevard André Maginot", + "language": "fr" + } + ] + }, + { + "offset": 97, + "names": [ + { + "value": "Boulevard André Maginot", + "language": "fr" + }, + { + "value": "Place Jean Cocteau", + "language": "fr" + } + ] + }, + { + "offset": 98, + "names": [ + { + "value": "Place Mazelle", + "language": "fr" + } + ] + }, + { + "offset": 110, + "names": [ + { + "value": "Passage de Plantières", + "language": "fr" + } + ] + }, + { + "offset": 119 + }, + { + "offset": 127, + "names": [ + { + "value": "Avenue de lAmphithéâtre", + "language": "fr" + } + ] + }, + { + "offset": 146, + "names": [ + { + "value": "Rue aux Arènes", + "language": "fr" + } + ] + }, + { + "offset": 192, + "names": [ + { + "value": "Rue Saint-Pierre", + "language": "fr" + } + ] + }, + { + "offset": 195, + "names": [ + { + "value": "Rue Émile Boilvin", + "language": "fr" + } + ] + }, + { + "offset": 199, + "names": [ + { + "value": "Rue Charles Sadoul", + "language": "fr" + } + ] + } + ], + "transport": { + "mode": "bicycle" + } + } + ] + } + ] +} diff --git a/tests/components/here_travel_time/test_sensor.py b/tests/components/here_travel_time/test_sensor.py index 157020d90d3..6d20a80bf15 100644 --- a/tests/components/here_travel_time/test_sensor.py +++ b/tests/components/here_travel_time/test_sensor.py @@ -749,3 +749,54 @@ async def test_transit_rate_limit(hass: HomeAssistant, caplog): await hass.async_block_till_done() assert hass.states.get("sensor.test_distance").state == "1.883" assert "Resetting update interval to" in caplog.text + + +@pytest.mark.usefixtures("bike_response") +async def test_multiple_sections( + hass: HomeAssistant, +): + """Test that multiple sections are handled correctly.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="0123456789", + data={ + CONF_ORIGIN_LATITUDE: float(ORIGIN_LATITUDE), + CONF_ORIGIN_LONGITUDE: float(ORIGIN_LONGITUDE), + CONF_DESTINATION_LATITUDE: float(DESTINATION_LATITUDE), + CONF_DESTINATION_LONGITUDE: float(DESTINATION_LONGITUDE), + CONF_API_KEY: API_KEY, + CONF_MODE: TRAVEL_MODE_BICYCLE, + CONF_NAME: "test", + }, + options=DEFAULT_OPTIONS, + ) + entry.add_to_hass(hass) + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() + + duration = hass.states.get("sensor.test_duration") + assert duration.state == "18" + + assert float(hass.states.get("sensor.test_distance").state) == pytest.approx(3.583) + assert hass.states.get("sensor.test_duration_in_traffic").state == "18" + assert hass.states.get("sensor.test_origin").state == "Chemin de Halage" + assert ( + hass.states.get("sensor.test_origin").attributes.get(ATTR_LATITUDE) + == "49.1260894" + ) + assert ( + hass.states.get("sensor.test_origin").attributes.get(ATTR_LONGITUDE) + == "6.1843356" + ) + + assert hass.states.get("sensor.test_destination").state == "Rue Charles Sadoul" + assert ( + hass.states.get("sensor.test_destination").attributes.get(ATTR_LATITUDE) + == "49.1025668" + ) + assert ( + hass.states.get("sensor.test_destination").attributes.get(ATTR_LONGITUDE) + == "6.1768518" + )