Add sensor platform to Proximity (#101497)

* add sensor platform

* transl. of distance already covered by dev.class

* add untested files to .coveragerc

* add missing state translations

* remove translation key for distance sensor

* proximity entity do not use HA number system

* fix

* extend tests

* make const final to be usable as key for TypedDict

* remove proximity from .coveragerc

* replace typeddict by simple dict definition

* make black happy

* rework to create proximity sensor for each tracked entity and always recalculate all entites

* apply review comments

* move direction of travel calc out of the loop

* make direction of travel an enum sensor

* remove unique_id from sensors

* don't set distance=0 when in monitored zone

* set None when direction is unknown

* keep distance 0 in case arrived for legacy entity

* exclude from nearest when in ignored zone

* keep distance=0 when arrived

* use description name for entity name

* remove uneeded typing

* uses consistent variable name

* fix debug messages

* use entity_id as loop var

* rename device_state to tracked_entity_state

* correct MRO for sensor entity classes
This commit is contained in:
Michael 2024-01-23 10:56:02 +01:00 committed by GitHub
parent fa63719161
commit eaa32146a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 898 additions and 390 deletions

View File

@ -5,15 +5,22 @@ import logging
import voluptuous as vol
from homeassistant.const import CONF_DEVICES, CONF_UNIT_OF_MEASUREMENT, CONF_ZONE
from homeassistant.const import (
CONF_DEVICES,
CONF_NAME,
CONF_UNIT_OF_MEASUREMENT,
CONF_ZONE,
)
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import (
ATTR_DIR_OF_TRAVEL,
ATTR_DIST_TO,
ATTR_NEAREST,
CONF_IGNORED_ZONES,
CONF_TOLERANCE,
@ -46,10 +53,12 @@ CONFIG_SCHEMA = vol.Schema(
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Get the zones and offsets from configuration.yaml."""
hass.data.setdefault(DOMAIN, {})
for zone, proximity_config in config[DOMAIN].items():
_LOGGER.debug("setup %s with config:%s", zone, proximity_config)
for friendly_name, proximity_config in config[DOMAIN].items():
_LOGGER.debug("setup %s with config:%s", friendly_name, proximity_config)
coordinator = ProximityDataUpdateCoordinator(hass, zone, proximity_config)
coordinator = ProximityDataUpdateCoordinator(
hass, friendly_name, proximity_config
)
async_track_state_change(
hass,
@ -58,12 +67,19 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
)
await coordinator.async_refresh()
hass.data[DOMAIN][zone] = coordinator
hass.data[DOMAIN][friendly_name] = coordinator
proximity = Proximity(hass, zone, coordinator)
proximity = Proximity(hass, friendly_name, coordinator)
await proximity.async_added_to_hass()
proximity.async_write_ha_state()
await async_load_platform(
hass,
"sensor",
DOMAIN,
{CONF_NAME: friendly_name, **proximity_config},
config,
)
return True
@ -91,12 +107,14 @@ class Proximity(CoordinatorEntity[ProximityDataUpdateCoordinator]):
@property
def state(self) -> str | int | float:
"""Return the state."""
return self.coordinator.data["dist_to_zone"]
return self.coordinator.data.proximity[ATTR_DIST_TO]
@property
def extra_state_attributes(self) -> dict[str, str]:
"""Return the state attributes."""
return {
ATTR_DIR_OF_TRAVEL: str(self.coordinator.data["dir_of_travel"]),
ATTR_NEAREST: str(self.coordinator.data["nearest"]),
ATTR_DIR_OF_TRAVEL: str(
self.coordinator.data.proximity[ATTR_DIR_OF_TRAVEL]
),
ATTR_NEAREST: str(self.coordinator.data.proximity[ATTR_NEAREST]),
}

View File

@ -1,10 +1,15 @@
"""Constants for Proximity integration."""
from typing import Final
from homeassistant.const import UnitOfLength
ATTR_DIR_OF_TRAVEL = "dir_of_travel"
ATTR_DIST_TO = "dist_to_zone"
ATTR_NEAREST = "nearest"
ATTR_DIR_OF_TRAVEL: Final = "dir_of_travel"
ATTR_DIST_TO: Final = "dist_to_zone"
ATTR_ENTITIES_DATA: Final = "entities_data"
ATTR_IN_IGNORED_ZONE: Final = "is_in_ignored_zone"
ATTR_NEAREST: Final = "nearest"
ATTR_PROXIMITY_DATA: Final = "proximity_data"
CONF_IGNORED_ZONES = "ignored_zones"
CONF_TOLERANCE = "tolerance"

View File

@ -2,11 +2,11 @@
from dataclasses import dataclass
import logging
from typing import TypedDict
from homeassistant.const import (
ATTR_LATITUDE,
ATTR_LONGITUDE,
ATTR_NAME,
CONF_DEVICES,
CONF_UNIT_OF_MEASUREMENT,
CONF_ZONE,
@ -19,6 +19,10 @@ from homeassistant.util.location import distance
from homeassistant.util.unit_conversion import DistanceConverter
from .const import (
ATTR_DIR_OF_TRAVEL,
ATTR_DIST_TO,
ATTR_IN_IGNORED_ZONE,
ATTR_NEAREST,
CONF_IGNORED_ZONES,
CONF_TOLERANCE,
DEFAULT_DIR_OF_TRAVEL,
@ -38,12 +42,22 @@ class StateChangedData:
new_state: State | None
class ProximityData(TypedDict):
"""ProximityData type class."""
@dataclass
class ProximityData:
"""ProximityCoordinatorData class."""
dist_to_zone: str | float
dir_of_travel: str | float
nearest: str | float
proximity: dict[str, str | float]
entities: dict[str, dict[str, str | int | None]]
DEFAULT_DATA = ProximityData(
{
ATTR_DIST_TO: DEFAULT_DIST_TO_ZONE,
ATTR_DIR_OF_TRAVEL: DEFAULT_DIR_OF_TRAVEL,
ATTR_NEAREST: DEFAULT_NEAREST,
},
{},
)
class ProximityDataUpdateCoordinator(DataUpdateCoordinator[ProximityData]):
@ -54,7 +68,7 @@ class ProximityDataUpdateCoordinator(DataUpdateCoordinator[ProximityData]):
) -> None:
"""Initialize the Proximity coordinator."""
self.ignored_zones: list[str] = config[CONF_IGNORED_ZONES]
self.proximity_devices: list[str] = config[CONF_DEVICES]
self.tracked_entities: list[str] = config[CONF_DEVICES]
self.tolerance: int = config[CONF_TOLERANCE]
self.proximity_zone: str = config[CONF_ZONE]
self.unit_of_measurement: str = config.get(
@ -69,11 +83,7 @@ class ProximityDataUpdateCoordinator(DataUpdateCoordinator[ProximityData]):
update_interval=None,
)
self.data = {
"dist_to_zone": DEFAULT_DIST_TO_ZONE,
"dir_of_travel": DEFAULT_DIR_OF_TRAVEL,
"nearest": DEFAULT_NEAREST,
}
self.data = DEFAULT_DATA
self.state_change_data: StateChangedData | None = None
@ -81,177 +91,216 @@ class ProximityDataUpdateCoordinator(DataUpdateCoordinator[ProximityData]):
self, entity: str, old_state: State | None, new_state: State | None
) -> None:
"""Fetch and process state change event."""
if new_state is None:
_LOGGER.debug("no new_state -> abort")
return
self.state_change_data = StateChangedData(entity, old_state, new_state)
await self.async_refresh()
async def _async_update_data(self) -> ProximityData:
"""Calculate Proximity data."""
if (
state_change_data := self.state_change_data
) is None or state_change_data.new_state is None:
return self.data
entity_name = state_change_data.new_state.name
devices_to_calculate = False
devices_in_zone = []
zone_state = self.hass.states.get(f"zone.{self.proximity_zone}")
proximity_latitude = (
zone_state.attributes.get(ATTR_LATITUDE) if zone_state else None
)
proximity_longitude = (
zone_state.attributes.get(ATTR_LONGITUDE) if zone_state else None
def _convert(self, value: float | str) -> float | str:
"""Round and convert given distance value."""
if isinstance(value, str):
return value
return round(
DistanceConverter.convert(
value,
UnitOfLength.METERS,
self.unit_of_measurement,
)
)
# Check for devices in the monitored zone.
for device in self.proximity_devices:
if (device_state := self.hass.states.get(device)) is None:
devices_to_calculate = True
continue
if device_state.state not in self.ignored_zones:
devices_to_calculate = True
# Check the location of all devices.
if (device_state.state).lower() == (self.proximity_zone).lower():
device_friendly = device_state.name
devices_in_zone.append(device_friendly)
# No-one to track so reset the entity.
if not devices_to_calculate:
_LOGGER.debug("no devices_to_calculate -> abort")
return {
"dist_to_zone": DEFAULT_DIST_TO_ZONE,
"dir_of_travel": DEFAULT_DIR_OF_TRAVEL,
"nearest": DEFAULT_NEAREST,
}
# At least one device is in the monitored zone so update the entity.
if devices_in_zone:
_LOGGER.debug("at least one device is in zone -> arrived")
return {
"dist_to_zone": 0,
"dir_of_travel": "arrived",
"nearest": ", ".join(devices_in_zone),
}
# We can't check proximity because latitude and longitude don't exist.
if "latitude" not in state_change_data.new_state.attributes:
_LOGGER.debug("no latitude and longitude -> reset")
return self.data
# Collect distances to the zone for all devices.
distances_to_zone: dict[str, float] = {}
for device in self.proximity_devices:
# Ignore devices in an ignored zone.
device_state = self.hass.states.get(device)
if not device_state or device_state.state in self.ignored_zones:
continue
# Ignore devices if proximity cannot be calculated.
if "latitude" not in device_state.attributes:
continue
# Calculate the distance to the proximity zone.
proximity = distance(
proximity_latitude,
proximity_longitude,
device_state.attributes[ATTR_LATITUDE],
device_state.attributes[ATTR_LONGITUDE],
def _calc_distance_to_zone(
self,
zone: State,
device: State,
latitude: float | None,
longitude: float | None,
) -> int | None:
if device.state.lower() == self.proximity_zone.lower():
_LOGGER.debug(
"%s: %s in zone -> distance=0",
self.friendly_name,
device.entity_id,
)
return 0
# Add the device and distance to a dictionary.
if proximity is None:
continue
distances_to_zone[device] = round(
DistanceConverter.convert(
proximity, UnitOfLength.METERS, self.unit_of_measurement
),
1,
if latitude is None or longitude is None:
_LOGGER.debug(
"%s: %s has no coordinates -> distance=None",
self.friendly_name,
device.entity_id,
)
return None
# Loop through each of the distances collected and work out the
# closest.
closest_device: str | None = None
dist_to_zone: float | None = None
distance_to_zone = distance(
zone.attributes[ATTR_LATITUDE],
zone.attributes[ATTR_LONGITUDE],
latitude,
longitude,
)
for device, zone in distances_to_zone.items():
if not dist_to_zone or zone < dist_to_zone:
closest_device = device
dist_to_zone = zone
# it is ensured, that distance can't be None, since zones must have lat/lon coordinates
assert distance_to_zone is not None
return round(distance_to_zone)
# If the closest device is one of the other devices.
if closest_device is not None and closest_device != state_change_data.entity_id:
_LOGGER.debug("closest device is one of the other devices -> unknown")
device_state = self.hass.states.get(closest_device)
assert device_state
return {
"dist_to_zone": round(distances_to_zone[closest_device]),
"dir_of_travel": "unknown",
"nearest": device_state.name,
}
def _calc_direction_of_travel(
self,
zone: State,
device: State,
old_latitude: float | None,
old_longitude: float | None,
new_latitude: float | None,
new_longitude: float | None,
) -> str | None:
if device.state.lower() == self.proximity_zone.lower():
_LOGGER.debug(
"%s: %s in zone -> direction_of_travel=arrived",
self.friendly_name,
device.entity_id,
)
return "arrived"
# Stop if we cannot calculate the direction of travel (i.e. we don't
# have a previous state and a current LAT and LONG).
if (
state_change_data.old_state is None
or "latitude" not in state_change_data.old_state.attributes
old_latitude is None
or old_longitude is None
or new_latitude is None
or new_longitude is None
):
_LOGGER.debug("no lat and lon in old_state -> unknown")
return {
"dist_to_zone": round(distances_to_zone[state_change_data.entity_id]),
"dir_of_travel": "unknown",
"nearest": entity_name,
}
return None
# Reset the variables
distance_travelled: float = 0
# Calculate the distance travelled.
old_distance = distance(
proximity_latitude,
proximity_longitude,
state_change_data.old_state.attributes[ATTR_LATITUDE],
state_change_data.old_state.attributes[ATTR_LONGITUDE],
zone.attributes[ATTR_LATITUDE],
zone.attributes[ATTR_LONGITUDE],
old_latitude,
old_longitude,
)
new_distance = distance(
proximity_latitude,
proximity_longitude,
state_change_data.new_state.attributes[ATTR_LATITUDE],
state_change_data.new_state.attributes[ATTR_LONGITUDE],
zone.attributes[ATTR_LATITUDE],
zone.attributes[ATTR_LONGITUDE],
new_latitude,
new_longitude,
)
assert new_distance is not None and old_distance is not None
# it is ensured, that distance can't be None, since zones must have lat/lon coordinates
assert old_distance is not None
assert new_distance is not None
distance_travelled = round(new_distance - old_distance, 1)
# Check for tolerance
if distance_travelled < self.tolerance * -1:
direction_of_travel = "towards"
elif distance_travelled > self.tolerance:
direction_of_travel = "away_from"
else:
direction_of_travel = "stationary"
return "towards"
# Update the proximity entity
dist_to: float | str
if dist_to_zone is not None:
dist_to = round(dist_to_zone)
else:
dist_to = DEFAULT_DIST_TO_ZONE
if distance_travelled > self.tolerance:
return "away_from"
_LOGGER.debug(
"%s updated: distance=%s: direction=%s: device=%s",
self.friendly_name,
dist_to,
direction_of_travel,
entity_name,
)
return "stationary"
return {
"dist_to_zone": dist_to,
"dir_of_travel": direction_of_travel,
"nearest": entity_name,
async def _async_update_data(self) -> ProximityData:
"""Calculate Proximity data."""
if (zone_state := self.hass.states.get(f"zone.{self.proximity_zone}")) is None:
_LOGGER.debug(
"%s: zone %s does not exist -> reset",
self.friendly_name,
self.proximity_zone,
)
return DEFAULT_DATA
entities_data = self.data.entities
# calculate distance for all tracked entities
for entity_id in self.tracked_entities:
if (tracked_entity_state := self.hass.states.get(entity_id)) is None:
if entities_data.pop(entity_id, None) is not None:
_LOGGER.debug(
"%s: %s does not exist -> remove", self.friendly_name, entity_id
)
continue
if entity_id not in entities_data:
_LOGGER.debug("%s: %s is new -> add", self.friendly_name, entity_id)
entities_data[entity_id] = {
ATTR_DIST_TO: None,
ATTR_DIR_OF_TRAVEL: None,
ATTR_NAME: tracked_entity_state.name,
ATTR_IN_IGNORED_ZONE: False,
}
entities_data[entity_id][ATTR_IN_IGNORED_ZONE] = (
tracked_entity_state.state.lower() in self.ignored_zones
)
entities_data[entity_id][ATTR_DIST_TO] = self._calc_distance_to_zone(
zone_state,
tracked_entity_state,
tracked_entity_state.attributes.get(ATTR_LATITUDE),
tracked_entity_state.attributes.get(ATTR_LONGITUDE),
)
if entities_data[entity_id][ATTR_DIST_TO] is None:
_LOGGER.debug(
"%s: %s has unknown distance got -> direction_of_travel=None",
self.friendly_name,
entity_id,
)
entities_data[entity_id][ATTR_DIR_OF_TRAVEL] = None
# calculate direction of travel only for last updated tracked entity
if (state_change_data := self.state_change_data) is not None and (
new_state := state_change_data.new_state
) is not None:
_LOGGER.debug(
"%s: calculate direction of travel for %s",
self.friendly_name,
state_change_data.entity_id,
)
if (old_state := state_change_data.old_state) is not None:
old_lat = old_state.attributes.get(ATTR_LATITUDE)
old_lon = old_state.attributes.get(ATTR_LONGITUDE)
else:
old_lat = None
old_lon = None
entities_data[state_change_data.entity_id][
ATTR_DIR_OF_TRAVEL
] = self._calc_direction_of_travel(
zone_state,
new_state,
old_lat,
old_lon,
new_state.attributes.get(ATTR_LATITUDE),
new_state.attributes.get(ATTR_LONGITUDE),
)
# takeover data for legacy proximity entity
proximity_data: dict[str, str | float] = {
ATTR_DIST_TO: DEFAULT_DIST_TO_ZONE,
ATTR_DIR_OF_TRAVEL: DEFAULT_DIR_OF_TRAVEL,
ATTR_NEAREST: DEFAULT_NEAREST,
}
for entity_data in entities_data.values():
if (distance_to := entity_data[ATTR_DIST_TO]) is None or entity_data[
ATTR_IN_IGNORED_ZONE
]:
continue
if isinstance((nearest_distance_to := proximity_data[ATTR_DIST_TO]), str):
_LOGGER.debug("set first entity_data: %s", entity_data)
proximity_data = {
ATTR_DIST_TO: distance_to,
ATTR_DIR_OF_TRAVEL: entity_data[ATTR_DIR_OF_TRAVEL] or "unknown",
ATTR_NEAREST: str(entity_data[ATTR_NAME]),
}
continue
if float(nearest_distance_to) > float(distance_to):
_LOGGER.debug("set closer entity_data: %s", entity_data)
proximity_data = {
ATTR_DIST_TO: distance_to,
ATTR_DIR_OF_TRAVEL: entity_data[ATTR_DIR_OF_TRAVEL] or "unknown",
ATTR_NEAREST: str(entity_data[ATTR_NAME]),
}
continue
if float(nearest_distance_to) == float(distance_to):
_LOGGER.debug("set equally close entity_data: %s", entity_data)
proximity_data[
ATTR_NEAREST
] = f"{proximity_data[ATTR_NEAREST]}, {str(entity_data[ATTR_NAME])}"
proximity_data[ATTR_DIST_TO] = self._convert(proximity_data[ATTR_DIST_TO])
return ProximityData(proximity_data, entities_data)

View File

@ -0,0 +1,138 @@
"""Support for Proximity sensors."""
from __future__ import annotations
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.const import CONF_NAME, UnitOfLength
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import ATTR_DIR_OF_TRAVEL, ATTR_DIST_TO, ATTR_NEAREST, DOMAIN
from .coordinator import ProximityDataUpdateCoordinator
SENSORS_PER_ENTITY: list[SensorEntityDescription] = [
SensorEntityDescription(
key=ATTR_DIST_TO,
name="Distance",
device_class=SensorDeviceClass.DISTANCE,
native_unit_of_measurement=UnitOfLength.METERS,
),
SensorEntityDescription(
key=ATTR_DIR_OF_TRAVEL,
name="Direction of travel",
translation_key=ATTR_DIR_OF_TRAVEL,
icon="mdi:compass-outline",
device_class=SensorDeviceClass.ENUM,
options=[
"arrived",
"away_from",
"stationary",
"towards",
],
),
]
SENSORS_PER_PROXIMITY: list[SensorEntityDescription] = [
SensorEntityDescription(
key=ATTR_NEAREST,
name="Nearest",
translation_key=ATTR_NEAREST,
icon="mdi:near-me",
),
]
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Proximity sensor platform."""
if discovery_info is None:
return
coordinator: ProximityDataUpdateCoordinator = hass.data[DOMAIN][
discovery_info[CONF_NAME]
]
entities: list[ProximitySensor | ProximityTrackedEntitySensor] = [
ProximitySensor(description, coordinator)
for description in SENSORS_PER_PROXIMITY
]
entities += [
ProximityTrackedEntitySensor(description, coordinator, tracked_entity_id)
for description in SENSORS_PER_ENTITY
for tracked_entity_id in coordinator.tracked_entities
]
async_add_entities(entities)
class ProximitySensor(CoordinatorEntity[ProximityDataUpdateCoordinator], SensorEntity):
"""Represents a Proximity sensor."""
_attr_has_entity_name = True
def __init__(
self,
description: SensorEntityDescription,
coordinator: ProximityDataUpdateCoordinator,
) -> None:
"""Initialize the proximity."""
super().__init__(coordinator)
self.entity_description = description
# entity name will be removed as soon as we have a config entry
# and can follow the entity naming guidelines
self._attr_name = f"{coordinator.friendly_name} {description.name}"
@property
def native_value(self) -> str | float | None:
"""Return native sensor value."""
if (
value := self.coordinator.data.proximity[self.entity_description.key]
) == "not set":
return None
return value
class ProximityTrackedEntitySensor(
CoordinatorEntity[ProximityDataUpdateCoordinator], SensorEntity
):
"""Represents a Proximity tracked entity sensor."""
_attr_has_entity_name = True
def __init__(
self,
description: SensorEntityDescription,
coordinator: ProximityDataUpdateCoordinator,
tracked_entity_id: str,
) -> None:
"""Initialize the proximity."""
super().__init__(coordinator)
self.entity_description = description
self.tracked_entity_id = tracked_entity_id
# entity name will be removed as soon as we have a config entry
# and can follow the entity naming guidelines
self._attr_name = (
f"{coordinator.friendly_name} {tracked_entity_id} {description.name}"
)
@property
def native_value(self) -> str | float | None:
"""Return native sensor value."""
if (data := self.coordinator.data.entities.get(self.tracked_entity_id)) is None:
return None
return data.get(self.entity_description.key)

View File

@ -1,3 +1,17 @@
{
"title": "Proximity"
"title": "Proximity",
"entity": {
"sensor": {
"dir_of_travel": {
"name": "Direction of travel",
"state": {
"arrived": "Arrived",
"away_from": "Away from",
"stationary": "Stationary",
"towards": "Towards"
}
},
"nearest": { "name": "Nearest device" }
}
}
}

View File

@ -1,10 +1,16 @@
"""The tests for the Proximity component."""
import pytest
from homeassistant.components.proximity import DOMAIN
from homeassistant.const import STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from homeassistant.util import slugify
async def test_proximities(hass: HomeAssistant) -> None:
@pytest.mark.parametrize(("friendly_name"), ["home", "home_test2", "work"])
async def test_proximities(hass: HomeAssistant, friendly_name: str) -> None:
"""Test a list of proximities."""
config = {
"proximity": {
@ -27,19 +33,28 @@ async def test_proximities(hass: HomeAssistant) -> None:
}
assert await async_setup_component(hass, DOMAIN, config)
await hass.async_block_till_done()
proximities = ["home", "home_test2", "work"]
# proximity entity
state = hass.states.get(f"proximity.{friendly_name}")
assert state.state == "not set"
assert state.attributes.get("nearest") == "not set"
assert state.attributes.get("dir_of_travel") == "not set"
hass.states.async_set(f"proximity.{friendly_name}", "0")
await hass.async_block_till_done()
state = hass.states.get(f"proximity.{friendly_name}")
assert state.state == "0"
for prox in proximities:
state = hass.states.get(f"proximity.{prox}")
assert state.state == "not set"
assert state.attributes.get("nearest") == "not set"
assert state.attributes.get("dir_of_travel") == "not set"
# sensor entities
state = hass.states.get(f"sensor.{friendly_name}_nearest")
assert state.state == STATE_UNKNOWN
hass.states.async_set(f"proximity.{prox}", "0")
await hass.async_block_till_done()
state = hass.states.get(f"proximity.{prox}")
assert state.state == "0"
for device in config["proximity"][friendly_name]["devices"]:
entity_base_name = f"sensor.{friendly_name}_{slugify(device)}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == STATE_UNKNOWN
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
async def test_proximities_setup(hass: HomeAssistant) -> None:
@ -58,31 +73,6 @@ async def test_proximities_setup(hass: HomeAssistant) -> None:
assert await async_setup_component(hass, DOMAIN, config)
async def test_proximity(hass: HomeAssistant) -> None:
"""Test the proximity."""
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1", "device_tracker.test2"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
state = hass.states.get("proximity.home")
assert state.state == "not set"
assert state.attributes.get("nearest") == "not set"
assert state.attributes.get("dir_of_travel") == "not set"
hass.states.async_set("proximity.home", "0")
await hass.async_block_till_done()
state = hass.states.get("proximity.home")
assert state.state == "0"
async def test_device_tracker_test1_in_zone(hass: HomeAssistant) -> None:
"""Test for tracker in zone."""
config = {
@ -103,11 +93,317 @@ async def test_device_tracker_test1_in_zone(hass: HomeAssistant) -> None:
{"friendly_name": "test1", "latitude": 2.1, "longitude": 1.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.state == "0"
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "arrived"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "0"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == "arrived"
async def test_device_tracker_test1_away(hass: HomeAssistant) -> None:
"""Test for tracker state away."""
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "11912010"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
async def test_device_tracker_test1_awayfurther(hass: HomeAssistant) -> None:
"""Test for tracker state away further."""
config_zones(hass)
await hass.async_block_till_done()
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "2218752"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 40.1, "longitude": 20.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "away_from"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "4625264"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == "away_from"
async def test_device_tracker_test1_awaycloser(hass: HomeAssistant) -> None:
"""Test for tracker state away closer."""
config_zones(hass)
await hass.async_block_till_done()
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 40.1, "longitude": 20.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "4625264"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "towards"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "2218752"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == "towards"
async def test_all_device_trackers_in_ignored_zone(hass: HomeAssistant) -> None:
"""Test for tracker in ignored zone."""
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set("device_tracker.test1", "work", {"friendly_name": "test1"})
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.state == "not set"
assert state.attributes.get("nearest") == "not set"
assert state.attributes.get("dir_of_travel") == "not set"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == STATE_UNKNOWN
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == STATE_UNKNOWN
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
async def test_device_tracker_test1_no_coordinates(hass: HomeAssistant) -> None:
"""Test for tracker with no coordinates."""
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set(
"device_tracker.test1", "not_home", {"friendly_name": "test1"}
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "not set"
assert state.attributes.get("dir_of_travel") == "not set"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == STATE_UNKNOWN
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == STATE_UNKNOWN
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
async def test_device_tracker_test1_awayfurther_a_bit(hass: HomeAssistant) -> None:
"""Test for tracker states."""
assert await async_setup_component(
hass,
DOMAIN,
{
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": 1000,
"zone": "home",
}
}
},
)
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1000001, "longitude": 10.1000001},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "11912010"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1000002, "longitude": 10.1000002},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "stationary"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "11912010"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == "stationary"
async def test_device_trackers_in_zone(hass: HomeAssistant) -> None:
"""Test for trackers in zone."""
@ -135,6 +431,8 @@ async def test_device_trackers_in_zone(hass: HomeAssistant) -> None:
{"friendly_name": "test2", "latitude": 2.1, "longitude": 1.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.state == "0"
assert (state.attributes.get("nearest") == "test1, test2") or (
@ -142,153 +440,16 @@ async def test_device_trackers_in_zone(hass: HomeAssistant) -> None:
)
assert state.attributes.get("dir_of_travel") == "arrived"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1, test2"
async def test_device_tracker_test1_away(hass: HomeAssistant) -> None:
"""Test for tracker state away."""
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1},
)
await hass.async_block_till_done()
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
async def test_device_tracker_test1_awayfurther(hass: HomeAssistant) -> None:
"""Test for tracker state away further."""
config_zones(hass)
await hass.async_block_till_done()
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1},
)
await hass.async_block_till_done()
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 40.1, "longitude": 20.1},
)
await hass.async_block_till_done()
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "away_from"
async def test_device_tracker_test1_awaycloser(hass: HomeAssistant) -> None:
"""Test for tracker state away closer."""
config_zones(hass)
await hass.async_block_till_done()
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 40.1, "longitude": 20.1},
)
await hass.async_block_till_done()
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1},
)
await hass.async_block_till_done()
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "towards"
async def test_all_device_trackers_in_ignored_zone(hass: HomeAssistant) -> None:
"""Test for tracker in ignored zone."""
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set("device_tracker.test1", "work", {"friendly_name": "test1"})
await hass.async_block_till_done()
state = hass.states.get("proximity.home")
assert state.state == "not set"
assert state.attributes.get("nearest") == "not set"
assert state.attributes.get("dir_of_travel") == "not set"
async def test_device_tracker_test1_no_coordinates(hass: HomeAssistant) -> None:
"""Test for tracker with no coordinates."""
config = {
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": "1",
}
}
}
assert await async_setup_component(hass, DOMAIN, config)
hass.states.async_set(
"device_tracker.test1", "not_home", {"friendly_name": "test1"}
)
await hass.async_block_till_done()
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "not set"
assert state.attributes.get("dir_of_travel") == "not set"
for device in ["device_tracker.test1", "device_tracker.test2"]:
entity_base_name = f"sensor.home_{slugify(device)}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "0"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == "arrived"
async def test_device_tracker_test1_awayfurther_than_test2_first_test1(
@ -328,20 +489,56 @@ async def test_device_tracker_test1_awayfurther_than_test2_first_test1(
{"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "2218752"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
entity_base_name = f"sensor.home_{slugify('device_tracker.test2')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == STATE_UNKNOWN
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
hass.states.async_set(
"device_tracker.test2",
"not_home",
{"friendly_name": "test2", "latitude": 40.1, "longitude": 20.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "2218752"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
entity_base_name = f"sensor.home_{slugify('device_tracker.test2')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "4625264"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
async def test_device_tracker_test1_awayfurther_than_test2_first_test2(
hass: HomeAssistant,
@ -378,20 +575,56 @@ async def test_device_tracker_test1_awayfurther_than_test2_first_test2(
{"friendly_name": "test2", "latitude": 40.1, "longitude": 20.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test2"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test2"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == STATE_UNKNOWN
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
entity_base_name = f"sensor.home_{slugify('device_tracker.test2')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "4625264"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "2218752"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
entity_base_name = f"sensor.home_{slugify('device_tracker.test2')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "4625264"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
async def test_device_tracker_test1_awayfurther_test2_in_ignored_zone(
hass: HomeAssistant,
@ -423,10 +656,28 @@ async def test_device_tracker_test1_awayfurther_test2_in_ignored_zone(
{"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "11912010"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
entity_base_name = f"sensor.home_{slugify('device_tracker.test2')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == STATE_UNKNOWN
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
async def test_device_tracker_test1_awayfurther_test2_first(
hass: HomeAssistant,
@ -489,47 +740,26 @@ async def test_device_tracker_test1_awayfurther_test2_first(
hass.states.async_set("device_tracker.test1", "work", {"friendly_name": "test1"})
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test2"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test2"
async def test_device_tracker_test1_awayfurther_a_bit(hass: HomeAssistant) -> None:
"""Test for tracker states."""
assert await async_setup_component(
hass,
DOMAIN,
{
"proximity": {
"home": {
"ignored_zones": ["work"],
"devices": ["device_tracker.test1"],
"tolerance": 1000,
"zone": "home",
}
}
},
)
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == STATE_UNKNOWN
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1000001, "longitude": 10.1000001},
)
await hass.async_block_till_done()
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
hass.states.async_set(
"device_tracker.test1",
"not_home",
{"friendly_name": "test1", "latitude": 20.1000002, "longitude": 10.1000002},
)
await hass.async_block_till_done()
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "stationary"
entity_base_name = f"sensor.home_{slugify('device_tracker.test2')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "2218752"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
async def test_device_tracker_test1_nearest_after_test2_in_ignored_zone(
@ -568,30 +798,84 @@ async def test_device_tracker_test1_nearest_after_test2_in_ignored_zone(
{"friendly_name": "test1", "latitude": 20.1, "longitude": 10.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "2218752"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
entity_base_name = f"sensor.home_{slugify('device_tracker.test2')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == STATE_UNKNOWN
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
hass.states.async_set(
"device_tracker.test2",
"not_home",
{"friendly_name": "test2", "latitude": 10.1, "longitude": 5.1},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test2"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test2"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "2218752"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
entity_base_name = f"sensor.home_{slugify('device_tracker.test2')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "989156"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
hass.states.async_set(
"device_tracker.test2",
"work",
{"friendly_name": "test2", "latitude": 12.6, "longitude": 7.6},
)
await hass.async_block_till_done()
# proximity entity
state = hass.states.get("proximity.home")
assert state.attributes.get("nearest") == "test1"
assert state.attributes.get("dir_of_travel") == "unknown"
# sensor entities
state = hass.states.get("sensor.home_nearest")
assert state.state == "test1"
entity_base_name = f"sensor.home_{slugify('device_tracker.test1')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "2218752"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == STATE_UNKNOWN
entity_base_name = f"sensor.home_{slugify('device_tracker.test2')}"
state = hass.states.get(f"{entity_base_name}_distance")
assert state.state == "1364567"
state = hass.states.get(f"{entity_base_name}_direction_of_travel")
assert state.state == "away_from"
def config_zones(hass):
"""Set up zones for test."""