Use SensorDeviceClass.DISTANCE for here_travel_time (#79159)

* Use SensorDeviceClass.DISTANCE in here_travel_time

* Removed unused import

* Use explicit units and conversion
This commit is contained in:
Kevin Stillhammer 2022-11-25 12:30:33 +01:00 committed by GitHub
parent c715035016
commit 5257875ac6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 48 additions and 189 deletions

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import logging
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_UNIT_SYSTEM, Platform
from homeassistant.const import CONF_API_KEY, CONF_MODE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.util import dt
@ -55,7 +55,6 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
origin_entity_id=config_entry.data.get(CONF_ORIGIN_ENTITY_ID),
travel_mode=config_entry.data[CONF_MODE],
route_mode=config_entry.options[CONF_ROUTE_MODE],
units=config_entry.options[CONF_UNIT_SYSTEM],
arrival=arrival,
departure=departure,
)

View File

@ -21,9 +21,8 @@ from homeassistant.const import (
CONF_LONGITUDE,
CONF_MODE,
CONF_NAME,
CONF_UNIT_SYSTEM,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.selector import (
@ -31,7 +30,6 @@ from homeassistant.helpers.selector import (
LocationSelector,
TimeSelector,
)
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
from .const import (
CONF_ARRIVAL_TIME,
@ -47,18 +45,21 @@ from .const import (
CONF_ROUTE_MODE,
DEFAULT_NAME,
DOMAIN,
IMPERIAL_UNITS,
METRIC_UNITS,
ROUTE_MODE_FASTEST,
ROUTE_MODES,
TRAVEL_MODE_CAR,
TRAVEL_MODE_PUBLIC,
TRAVEL_MODES,
UNITS,
)
_LOGGER = logging.getLogger(__name__)
DEFAULT_OPTIONS = {
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_ARRIVAL_TIME: None,
CONF_DEPARTURE_TIME: None,
}
async def async_validate_api_key(api_key: str) -> None:
"""Validate the user input allows us to connect."""
@ -90,19 +91,6 @@ def get_user_step_schema(data: dict[str, Any]) -> vol.Schema:
)
def default_options(hass: HomeAssistant) -> dict[str, str | None]:
"""Get the default options."""
default = {
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_ARRIVAL_TIME: None,
CONF_DEPARTURE_TIME: None,
CONF_UNIT_SYSTEM: METRIC_UNITS,
}
if hass.config.units is US_CUSTOMARY_SYSTEM:
default[CONF_UNIT_SYSTEM] = IMPERIAL_UNITS
return default
class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for HERE Travel Time."""
@ -198,7 +186,7 @@ class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_create_entry(
title=self._config[CONF_NAME],
data=self._config,
options=default_options(self.hass),
options=DEFAULT_OPTIONS,
)
schema = vol.Schema(
{
@ -227,7 +215,7 @@ class HERETravelTimeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_create_entry(
title=self._config[CONF_NAME],
data=self._config,
options=default_options(self.hass),
options=DEFAULT_OPTIONS,
)
schema = vol.Schema(
{vol.Required(CONF_DESTINATION_ENTITY_ID): EntitySelector()}
@ -254,21 +242,14 @@ class HERETravelTimeOptionsFlow(config_entries.OptionsFlow):
menu_options=["departure_time", "arrival_time", "no_time"],
)
defaults = default_options(self.hass)
schema = vol.Schema(
{
vol.Optional(
CONF_ROUTE_MODE,
default=self.config_entry.options.get(
CONF_ROUTE_MODE, defaults[CONF_ROUTE_MODE]
CONF_ROUTE_MODE, DEFAULT_OPTIONS[CONF_ROUTE_MODE]
),
): vol.In(ROUTE_MODES),
vol.Optional(
CONF_UNIT_SYSTEM,
default=self.config_entry.options.get(
CONF_UNIT_SYSTEM, defaults[CONF_UNIT_SYSTEM]
),
): vol.In(UNITS),
}
)

View File

@ -51,17 +51,11 @@ ICONS = {
TRAVEL_MODE_TRUCK: ICON_TRUCK,
}
IMPERIAL_UNITS = "imperial"
METRIC_UNITS = "metric"
UNITS = [METRIC_UNITS, IMPERIAL_UNITS]
ATTR_DURATION = "duration"
ATTR_DISTANCE = "distance"
ATTR_ORIGIN = "origin"
ATTR_DESTINATION = "destination"
ATTR_UNIT_SYSTEM = "unit_system"
ATTR_DURATION_IN_TRAFFIC = "duration_in_traffic"
ATTR_ORIGIN_NAME = "origin_name"
ATTR_DESTINATION_NAME = "destination_name"

View File

@ -10,7 +10,7 @@ import here_transit
from here_transit import HERETransitApi
import voluptuous as vol
from homeassistant.const import ATTR_ATTRIBUTION, LENGTH_METERS, LENGTH_MILES
from homeassistant.const import ATTR_ATTRIBUTION, UnitOfLength
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.location import find_coordinates
@ -28,7 +28,6 @@ from .const import (
ATTR_ORIGIN_NAME,
DEFAULT_SCAN_INTERVAL,
DOMAIN,
IMPERIAL_UNITS,
ROUTE_MODE_FASTEST,
)
from .model import HERETravelTimeConfig, HERETravelTimeData
@ -100,13 +99,9 @@ class HERERoutingDataUpdateCoordinator(DataUpdateCoordinator):
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 = summary["length"]
if self.config.units == IMPERIAL_UNITS:
# Convert to miles.
distance = DistanceConverter.convert(distance, LENGTH_METERS, LENGTH_MILES)
else:
# Convert to kilometers
distance = distance / 1000
distance: float = DistanceConverter.convert(
summary["length"], UnitOfLength.METERS, UnitOfLength.KILOMETERS
)
origin_name: str | None = None
if (names := section["spans"][0].get("names")) is not None:
origin_name = names[0]["value"]
@ -189,18 +184,14 @@ class HERETransitDataUpdateCoordinator(DataUpdateCoordinator):
mapped_destination_lon: float = sections[-1]["arrival"]["place"]["location"][
"lng"
]
distance: float = sum(
section["travelSummary"]["length"] for section in sections
distance: float = DistanceConverter.convert(
sum(section["travelSummary"]["length"] for section in sections),
UnitOfLength.METERS,
UnitOfLength.KILOMETERS,
)
duration: float = sum(
section["travelSummary"]["duration"] for section in sections
)
if self.config.units == IMPERIAL_UNITS:
# Convert to miles.
distance = DistanceConverter.convert(distance, LENGTH_METERS, LENGTH_MILES)
else:
# Convert to kilometers
distance = distance / 1000
return HERETravelTimeData(
{
ATTR_ATTRIBUTION: attribution,

View File

@ -31,6 +31,5 @@ class HERETravelTimeConfig:
origin_entity_id: str | None
travel_mode: str
route_mode: str
units: str
arrival: time | None
departure: time | None

View File

@ -6,6 +6,7 @@ from datetime import timedelta
from typing import Any
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
@ -17,9 +18,8 @@ from homeassistant.const import (
ATTR_LONGITUDE,
CONF_MODE,
CONF_NAME,
LENGTH_KILOMETERS,
LENGTH_MILES,
TIME_MINUTES,
UnitOfLength,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType
@ -39,7 +39,6 @@ from .const import (
DOMAIN,
ICON_CAR,
ICONS,
IMPERIAL_UNITS,
)
from .coordinator import HERERoutingDataUpdateCoordinator
@ -63,6 +62,14 @@ def sensor_descriptions(travel_mode: str) -> tuple[SensorEntityDescription, ...]
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=TIME_MINUTES,
),
SensorEntityDescription(
name="Distance",
icon=ICONS.get(travel_mode, ICON_CAR),
key=ATTR_DISTANCE,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=UnitOfLength.KILOMETERS,
),
)
@ -89,7 +96,6 @@ async def async_setup_entry(
)
sensors.append(OriginSensor(entry_id, name, coordinator))
sensors.append(DestinationSensor(entry_id, name, coordinator))
sensors.append(DistanceSensor(entry_id, name, coordinator))
async_add_entities(sensors)
@ -193,29 +199,3 @@ class DestinationSensor(HERETravelTimeSensor):
ATTR_LONGITUDE: self.coordinator.data[ATTR_DESTINATION].split(",")[1],
}
return None
class DistanceSensor(HERETravelTimeSensor):
"""Sensor holding information about the distance."""
def __init__(
self,
unique_id_prefix: str,
name: str,
coordinator: HERERoutingDataUpdateCoordinator,
) -> None:
"""Initialize the sensor."""
sensor_description = SensorEntityDescription(
name="Distance",
icon=ICONS.get(coordinator.config.travel_mode, ICON_CAR),
key=ATTR_DISTANCE,
state_class=SensorStateClass.MEASUREMENT,
)
super().__init__(unique_id_prefix, name, sensor_description, coordinator)
@property
def native_unit_of_measurement(self) -> str | None:
"""Return the unit of measurement of the sensor."""
if self.coordinator.config.units == IMPERIAL_UNITS:
return LENGTH_MILES
return LENGTH_KILOMETERS

View File

@ -19,20 +19,8 @@ from homeassistant.components.here_travel_time.const import (
TRAVEL_MODE_CAR,
TRAVEL_MODE_PUBLIC,
)
from homeassistant.const import (
CONF_API_KEY,
CONF_MODE,
CONF_NAME,
CONF_UNIT_SYSTEM,
CONF_UNIT_SYSTEM_IMPERIAL,
CONF_UNIT_SYSTEM_METRIC,
)
from homeassistant.const import CONF_API_KEY, CONF_MODE, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.util.unit_system import (
METRIC_SYSTEM,
US_CUSTOMARY_SYSTEM,
UnitSystem,
)
from .const import (
API_KEY,
@ -98,7 +86,6 @@ async def option_init_result_fixture(hass: HomeAssistant) -> data_entry_flow.Flo
flow["flow_id"],
user_input={
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC,
},
)
return result
@ -229,21 +216,11 @@ async def test_step_destination_coordinates(
@pytest.mark.usefixtures("valid_response")
@pytest.mark.parametrize(
"unit_system, expected_unit_option",
[
(METRIC_SYSTEM, CONF_UNIT_SYSTEM_METRIC),
(US_CUSTOMARY_SYSTEM, CONF_UNIT_SYSTEM_IMPERIAL),
],
)
async def test_step_destination_entity(
hass: HomeAssistant,
origin_step_result: data_entry_flow.FlowResult,
unit_system: UnitSystem,
expected_unit_option: str,
) -> None:
"""Test the origin coordinates step."""
hass.config.units = unit_system
menu_result = await hass.config_entries.flow.async_configure(
origin_step_result["flow_id"], {"next_step_id": "destination_entity"}
)
@ -264,7 +241,6 @@ async def test_step_destination_entity(
CONF_MODE: TRAVEL_MODE_CAR,
}
assert entry.options == {
CONF_UNIT_SYSTEM: expected_unit_option,
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_ARRIVAL_TIME: None,
CONF_DEPARTURE_TIME: None,
@ -340,7 +316,6 @@ async def test_options_flow(hass: HomeAssistant) -> None:
result["flow_id"],
user_input={
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL,
},
)
@ -366,7 +341,6 @@ async def test_options_flow_arrival_time_step(
assert time_selector_result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
entry = hass.config_entries.async_entries(DOMAIN)[0]
assert entry.options == {
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC,
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_ARRIVAL_TIME: "08:00:00",
}
@ -391,7 +365,6 @@ async def test_options_flow_departure_time_step(
assert time_selector_result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
entry = hass.config_entries.async_entries(DOMAIN)[0]
assert entry.options == {
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC,
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_DEPARTURE_TIME: "08:00:00",
}
@ -409,6 +382,5 @@ async def test_options_flow_no_time_step(
assert menu_result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
entry = hass.config_entries.async_entries(DOMAIN)[0]
assert entry.options == {
CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_METRIC,
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
}

View File

@ -2,7 +2,7 @@
import pytest
from homeassistant.components.here_travel_time.config_flow import default_options
from homeassistant.components.here_travel_time.config_flow import DEFAULT_OPTIONS
from homeassistant.components.here_travel_time.const import DOMAIN
from homeassistant.core import HomeAssistant
@ -18,7 +18,7 @@ async def test_unload_entry(hass: HomeAssistant) -> None:
domain=DOMAIN,
unique_id="0123456789",
data=DEFAULT_CONFIG,
options=default_options(hass),
options=DEFAULT_OPTIONS,
)
entry.add_to_hass(hass)

View File

@ -11,7 +11,7 @@ from here_routing import (
)
import pytest
from homeassistant.components.here_travel_time.config_flow import default_options
from homeassistant.components.here_travel_time.config_flow import DEFAULT_OPTIONS
from homeassistant.components.here_travel_time.const import (
CONF_ARRIVAL_TIME,
CONF_DEPARTURE_TIME,
@ -27,8 +27,6 @@ from homeassistant.components.here_travel_time.const import (
ICON_CAR,
ICON_PEDESTRIAN,
ICON_TRUCK,
IMPERIAL_UNITS,
METRIC_UNITS,
ROUTE_MODE_FASTEST,
TRAVEL_MODE_BICYCLE,
TRAVEL_MODE_CAR,
@ -41,14 +39,10 @@ from homeassistant.const import (
ATTR_ICON,
ATTR_LATITUDE,
ATTR_LONGITUDE,
ATTR_UNIT_OF_MEASUREMENT,
CONF_API_KEY,
CONF_MODE,
CONF_NAME,
CONF_UNIT_SYSTEM,
EVENT_HOMEASSISTANT_START,
LENGTH_KILOMETERS,
LENGTH_MILES,
TIME_MINUTES,
)
from homeassistant.core import HomeAssistant
@ -66,51 +60,31 @@ from tests.common import MockConfigEntry
@pytest.mark.parametrize(
"mode,icon,unit_system,arrival_time,departure_time,expected_duration,expected_distance,expected_duration_in_traffic,expected_distance_unit",
"mode,icon,arrival_time,departure_time",
[
(
TRAVEL_MODE_CAR,
ICON_CAR,
"metric",
None,
None,
"26",
13.682,
"30",
LENGTH_KILOMETERS,
),
(
TRAVEL_MODE_BICYCLE,
ICON_BICYCLE,
"metric",
None,
None,
"26",
13.682,
"30",
LENGTH_KILOMETERS,
),
(
TRAVEL_MODE_PEDESTRIAN,
ICON_PEDESTRIAN,
"imperial",
None,
None,
"26",
8.5016,
"30",
LENGTH_MILES,
"08:00:00",
),
(
TRAVEL_MODE_TRUCK,
ICON_TRUCK,
"metric",
None,
"08:00:00",
"26",
13.682,
"30",
LENGTH_KILOMETERS,
),
],
)
@ -119,13 +93,8 @@ async def test_sensor(
hass: HomeAssistant,
mode,
icon,
unit_system,
arrival_time,
departure_time,
expected_duration,
expected_distance,
expected_duration_in_traffic,
expected_distance_unit,
):
"""Test that sensor works."""
entry = MockConfigEntry(
@ -144,7 +113,6 @@ async def test_sensor(
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_ARRIVAL_TIME: arrival_time,
CONF_DEPARTURE_TIME: departure_time,
CONF_UNIT_SYSTEM: unit_system,
},
)
entry.add_to_hass(hass)
@ -156,23 +124,10 @@ async def test_sensor(
duration = hass.states.get("sensor.test_duration")
assert duration.attributes.get("unit_of_measurement") == TIME_MINUTES
assert duration.attributes.get(ATTR_ICON) == icon
assert duration.state == expected_duration
assert duration.state == "26"
assert (
hass.states.get("sensor.test_duration_in_traffic").state
== expected_duration_in_traffic
)
assert float(hass.states.get("sensor.test_distance").state) == pytest.approx(
expected_distance
)
assert (
hass.states.get("sensor.test_distance").attributes.get(ATTR_UNIT_OF_MEASUREMENT)
== expected_distance_unit
)
assert (
hass.states.get("sensor.test_duration_in_traffic").state
== expected_duration_in_traffic
)
assert float(hass.states.get("sensor.test_distance").state) == pytest.approx(13.682)
assert hass.states.get("sensor.test_duration_in_traffic").state == "30"
assert hass.states.get("sensor.test_origin").state == "22nd St NW"
assert (
hass.states.get("sensor.test_origin").attributes.get(ATTR_LATITUDE)
@ -213,7 +168,7 @@ async def test_circular_ref(hass: HomeAssistant, caplog):
CONF_MODE: TRAVEL_MODE_TRUCK,
CONF_NAME: "test",
},
options=default_options(hass),
options=DEFAULT_OPTIONS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -226,16 +181,7 @@ async def test_circular_ref(hass: HomeAssistant, caplog):
@pytest.mark.usefixtures("valid_response")
@pytest.mark.parametrize(
"unit_system,expected_distance",
[
(METRIC_UNITS, "1.883"),
(IMPERIAL_UNITS, "1.1700419549829"),
],
)
async def test_public_transport(
hass: HomeAssistant, unit_system: str, expected_distance: str
):
async def test_public_transport(hass: HomeAssistant):
"""Test that public transport mode is handled."""
entry = MockConfigEntry(
domain=DOMAIN,
@ -253,7 +199,6 @@ async def test_public_transport(
CONF_ROUTE_MODE: ROUTE_MODE_FASTEST,
CONF_ARRIVAL_TIME: "08:00:00",
CONF_DEPARTURE_TIME: None,
CONF_UNIT_SYSTEM: unit_system,
},
)
entry.add_to_hass(hass)
@ -267,9 +212,7 @@ async def test_public_transport(
hass.states.get("sensor.test_duration").attributes.get(ATTR_ATTRIBUTION)
== "http://creativecommons.org/licenses/by/3.0/it/,Some line names used in this product or service were edited to align with official transportation maps."
)
assert hass.states.get("sensor.test_distance").state == pytest.approx(
expected_distance
)
assert hass.states.get("sensor.test_distance").state == "1.883"
@pytest.mark.usefixtures("no_attribution_response")
@ -287,7 +230,7 @@ async def test_no_attribution_response(hass: HomeAssistant):
CONF_MODE: TRAVEL_MODE_PUBLIC,
CONF_NAME: "test",
},
options=default_options(hass),
options=DEFAULT_OPTIONS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -333,7 +276,7 @@ async def test_entity_ids(hass: HomeAssistant, valid_response: MagicMock):
CONF_MODE: TRAVEL_MODE_TRUCK,
CONF_NAME: "test",
},
options=default_options(hass),
options=DEFAULT_OPTIONS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -370,7 +313,7 @@ async def test_destination_entity_not_found(hass: HomeAssistant, caplog):
CONF_MODE: TRAVEL_MODE_TRUCK,
CONF_NAME: "test",
},
options=default_options(hass),
options=DEFAULT_OPTIONS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -396,7 +339,7 @@ async def test_origin_entity_not_found(hass: HomeAssistant, caplog):
CONF_MODE: TRAVEL_MODE_TRUCK,
CONF_NAME: "test",
},
options=default_options(hass),
options=DEFAULT_OPTIONS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -426,7 +369,7 @@ async def test_invalid_destination_entity_state(hass: HomeAssistant, caplog):
CONF_MODE: TRAVEL_MODE_TRUCK,
CONF_NAME: "test",
},
options=default_options(hass),
options=DEFAULT_OPTIONS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -456,7 +399,7 @@ async def test_invalid_origin_entity_state(hass: HomeAssistant, caplog):
CONF_MODE: TRAVEL_MODE_TRUCK,
CONF_NAME: "test",
},
options=default_options(hass),
options=DEFAULT_OPTIONS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
@ -488,7 +431,7 @@ async def test_route_not_found(hass: HomeAssistant, caplog):
CONF_MODE: TRAVEL_MODE_TRUCK,
CONF_NAME: "test",
},
options=default_options(hass),
options=DEFAULT_OPTIONS,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)