mirror of
https://github.com/home-assistant/core.git
synced 2026-04-01 05:16:12 +02:00
Compare commits
6 Commits
dev
...
device_tra
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
002bf80a1f | ||
|
|
8acc96b9ba | ||
|
|
db379cb79f | ||
|
|
af2899f22a | ||
|
|
8a13da4e9d | ||
|
|
3bd7e93285 |
@@ -21,6 +21,7 @@ from .const import ( # noqa: F401
|
||||
ATTR_DEV_ID,
|
||||
ATTR_GPS,
|
||||
ATTR_HOST_NAME,
|
||||
ATTR_IN_ZONES,
|
||||
ATTR_IP,
|
||||
ATTR_LOCATION_NAME,
|
||||
ATTR_MAC,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import final
|
||||
from typing import Any, final
|
||||
|
||||
from propcache.api import cached_property
|
||||
|
||||
@@ -18,7 +18,7 @@ from homeassistant.const import (
|
||||
STATE_NOT_HOME,
|
||||
EntityCategory,
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.core import Event, HomeAssistant, State, callback
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.device_registry import (
|
||||
DeviceInfo,
|
||||
@@ -33,6 +33,7 @@ from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import (
|
||||
ATTR_HOST_NAME,
|
||||
ATTR_IN_ZONES,
|
||||
ATTR_IP,
|
||||
ATTR_MAC,
|
||||
ATTR_SOURCE_TYPE,
|
||||
@@ -223,6 +224,9 @@ class TrackerEntity(
|
||||
_attr_longitude: float | None = None
|
||||
_attr_source_type: SourceType = SourceType.GPS
|
||||
|
||||
__active_zone: State | None = None
|
||||
__in_zones: list[str] | None = None
|
||||
|
||||
@cached_property
|
||||
def should_poll(self) -> bool:
|
||||
"""No polling for entities that have location pushed."""
|
||||
@@ -256,6 +260,18 @@ class TrackerEntity(
|
||||
"""Return longitude value of the device."""
|
||||
return self._attr_longitude
|
||||
|
||||
@callback
|
||||
def _async_write_ha_state(self) -> None:
|
||||
"""Calculate active zones."""
|
||||
if self.latitude is not None and self.longitude is not None:
|
||||
self.__active_zone, self.__in_zones = zone.async_in_zones(
|
||||
self.hass, self.latitude, self.longitude, self.location_accuracy
|
||||
)
|
||||
else:
|
||||
self.__active_zone = None
|
||||
self.__in_zones = None
|
||||
super()._async_write_ha_state()
|
||||
|
||||
@property
|
||||
def state(self) -> str | None:
|
||||
"""Return the state of the device."""
|
||||
@@ -263,9 +279,7 @@ class TrackerEntity(
|
||||
return self.location_name
|
||||
|
||||
if self.latitude is not None and self.longitude is not None:
|
||||
zone_state = zone.async_active_zone(
|
||||
self.hass, self.latitude, self.longitude, self.location_accuracy
|
||||
)
|
||||
zone_state = self.__active_zone
|
||||
if zone_state is None:
|
||||
state = STATE_NOT_HOME
|
||||
elif zone_state.entity_id == zone.ENTITY_ID_HOME:
|
||||
@@ -278,12 +292,13 @@ class TrackerEntity(
|
||||
|
||||
@final
|
||||
@property
|
||||
def state_attributes(self) -> dict[str, StateType]:
|
||||
def state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the device state attributes."""
|
||||
attr: dict[str, StateType] = {}
|
||||
attr: dict[str, Any] = {ATTR_IN_ZONES: []}
|
||||
attr.update(super().state_attributes)
|
||||
|
||||
if self.latitude is not None and self.longitude is not None:
|
||||
attr[ATTR_IN_ZONES] = self.__in_zones or []
|
||||
attr[ATTR_LATITUDE] = self.latitude
|
||||
attr[ATTR_LONGITUDE] = self.longitude
|
||||
attr[ATTR_GPS_ACCURACY] = self.location_accuracy
|
||||
|
||||
@@ -43,6 +43,7 @@ ATTR_BATTERY: Final = "battery"
|
||||
ATTR_DEV_ID: Final = "dev_id"
|
||||
ATTR_GPS: Final = "gps"
|
||||
ATTR_HOST_NAME: Final = "host_name"
|
||||
ATTR_IN_ZONES: Final = "in_zones"
|
||||
ATTR_LOCATION_NAME: Final = "location_name"
|
||||
ATTR_MAC: Final = "mac"
|
||||
ATTR_SOURCE_TYPE: Final = "source_type"
|
||||
|
||||
@@ -113,17 +113,21 @@ DATA_ZONE_STORAGE_COLLECTION: HassKey[ZoneStorageCollection] = HassKey(DOMAIN)
|
||||
DATA_ZONE_ENTITY_IDS: HassKey[list[str]] = HassKey(ZONE_ENTITY_IDS)
|
||||
|
||||
|
||||
@bind_hass
|
||||
def async_active_zone(
|
||||
def async_in_zones(
|
||||
hass: HomeAssistant, latitude: float, longitude: float, radius: float = 0
|
||||
) -> State | None:
|
||||
"""Find the active zone for given latitude, longitude.
|
||||
) -> tuple[State | None, list[str]]:
|
||||
"""Find zones which contain the given latitude and longitude.
|
||||
|
||||
Returns a tuple of the closest active zone and a list of all zones which
|
||||
contain the given latitude and longitude. The list of zones is sorted by
|
||||
distance and then by radius so that the closest and smallest zone is first.
|
||||
|
||||
This method must be run in the event loop.
|
||||
"""
|
||||
# Sort entity IDs so that we are deterministic if equal distance to 2 zones
|
||||
min_dist: float = sys.maxsize
|
||||
closest: State | None = None
|
||||
zones: list[tuple[str, float, float]] = []
|
||||
|
||||
# This can be called before async_setup by device tracker
|
||||
zone_entity_ids = hass.data.get(DATA_ZONE_ENTITY_IDS, ())
|
||||
@@ -133,10 +137,12 @@ def async_active_zone(
|
||||
not (zone := hass.states.get(entity_id))
|
||||
# Skip unavailable zones
|
||||
or zone.state == STATE_UNAVAILABLE
|
||||
# Skip passive zones
|
||||
or (zone_attrs := zone.attributes).get(ATTR_PASSIVE)
|
||||
):
|
||||
continue
|
||||
zone_attrs = zone.attributes
|
||||
if (
|
||||
# Skip zones where we cannot calculate distance
|
||||
or (
|
||||
(
|
||||
zone_dist := distance(
|
||||
latitude,
|
||||
longitude,
|
||||
@@ -151,6 +157,12 @@ def async_active_zone(
|
||||
):
|
||||
continue
|
||||
|
||||
zones.append((zone.entity_id, zone_dist, zone_radius))
|
||||
|
||||
# Skip passive zones
|
||||
if zone_attrs.get(ATTR_PASSIVE):
|
||||
continue
|
||||
|
||||
# If have a closest and its not closer than the closest skip it
|
||||
if closest and not (
|
||||
zone_dist < min_dist
|
||||
@@ -166,7 +178,20 @@ def async_active_zone(
|
||||
min_dist = zone_dist
|
||||
closest = zone
|
||||
|
||||
return closest
|
||||
# Sort by distance and then by radius so the closest and smallest zone is first.
|
||||
zones.sort(key=lambda x: (x[1], x[2]))
|
||||
return (closest, [itm[0] for itm in zones])
|
||||
|
||||
|
||||
@bind_hass
|
||||
def async_active_zone(
|
||||
hass: HomeAssistant, latitude: float, longitude: float, radius: float = 0
|
||||
) -> State | None:
|
||||
"""Find the active zone for given latitude, longitude.
|
||||
|
||||
This method must be run in the event loop.
|
||||
"""
|
||||
return async_in_zones(hass, latitude, longitude, radius)[0]
|
||||
|
||||
|
||||
@callback
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
'friendly_name': 'Test Vehicle',
|
||||
'gps_accuracy': 6.0,
|
||||
'icon': 'mdi:car',
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 50.1109221,
|
||||
'longitude': 8.6821267,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -7,6 +7,7 @@ import pytest
|
||||
|
||||
from homeassistant.components.device_tracker import (
|
||||
ATTR_HOST_NAME,
|
||||
ATTR_IN_ZONES,
|
||||
ATTR_IP,
|
||||
ATTR_MAC,
|
||||
ATTR_SOURCE_TYPE,
|
||||
@@ -384,6 +385,7 @@ async def test_load_unload_entry(
|
||||
{
|
||||
ATTR_SOURCE_TYPE: SourceType.GPS,
|
||||
ATTR_GPS_ACCURACY: 0,
|
||||
ATTR_IN_ZONES: [],
|
||||
ATTR_LATITUDE: 1.0,
|
||||
ATTR_LONGITUDE: 2.0,
|
||||
},
|
||||
@@ -397,6 +399,7 @@ async def test_load_unload_entry(
|
||||
{
|
||||
ATTR_SOURCE_TYPE: SourceType.GPS,
|
||||
ATTR_GPS_ACCURACY: 0,
|
||||
ATTR_IN_ZONES: ["zone.home"],
|
||||
ATTR_LATITUDE: 50.0,
|
||||
ATTR_LONGITUDE: 60.0,
|
||||
},
|
||||
@@ -410,6 +413,7 @@ async def test_load_unload_entry(
|
||||
{
|
||||
ATTR_SOURCE_TYPE: SourceType.GPS,
|
||||
ATTR_GPS_ACCURACY: 0,
|
||||
ATTR_IN_ZONES: ["zone.other_zone", "zone.other_zone_larger"],
|
||||
ATTR_LATITUDE: -50.0,
|
||||
ATTR_LONGITUDE: -60.0,
|
||||
},
|
||||
@@ -422,6 +426,7 @@ async def test_load_unload_entry(
|
||||
"zen_zone",
|
||||
{
|
||||
ATTR_SOURCE_TYPE: SourceType.GPS,
|
||||
ATTR_IN_ZONES: [],
|
||||
},
|
||||
),
|
||||
(
|
||||
@@ -430,7 +435,10 @@ async def test_load_unload_entry(
|
||||
None,
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
{ATTR_SOURCE_TYPE: SourceType.GPS},
|
||||
{
|
||||
ATTR_SOURCE_TYPE: SourceType.GPS,
|
||||
ATTR_IN_ZONES: [],
|
||||
},
|
||||
),
|
||||
(
|
||||
100,
|
||||
@@ -438,7 +446,11 @@ async def test_load_unload_entry(
|
||||
None,
|
||||
None,
|
||||
STATE_UNKNOWN,
|
||||
{ATTR_BATTERY_LEVEL: 100, ATTR_SOURCE_TYPE: SourceType.GPS},
|
||||
{
|
||||
ATTR_BATTERY_LEVEL: 100,
|
||||
ATTR_SOURCE_TYPE: SourceType.GPS,
|
||||
ATTR_IN_ZONES: [],
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -463,6 +475,11 @@ async def test_tracker_entity_state(
|
||||
"0",
|
||||
{ATTR_LATITUDE: -50.0, ATTR_LONGITUDE: -60.0, ATTR_RADIUS: 300},
|
||||
)
|
||||
hass.states.async_set(
|
||||
"zone.other_zone_larger",
|
||||
"0",
|
||||
{ATTR_LATITUDE: -50.0, ATTR_LONGITUDE: -60.0, ATTR_RADIUS: 500},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
# Write state again to ensure the zone state is taken into account.
|
||||
tracker_entity.async_write_ha_state()
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
'entity_picture': 'http://res.cloudinary.com/iot-venture/image/upload/v1717594357/kyaqq7nfitrdvaoakb8s.jpg',
|
||||
'friendly_name': 'Fluffy',
|
||||
'gps_accuracy': 10.0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 52.520008,
|
||||
'longitude': 13.404954,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Test Mower 1',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 35.5402913,
|
||||
'longitude': -82.5527055,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'mock model',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 25.0,
|
||||
'longitude': -71.0,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': '2021 Honda Accord',
|
||||
'gps_accuracy': 10,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 37.7749,
|
||||
'longitude': -122.4194,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -51,53 +51,83 @@ async def setup_zone(hass: HomeAssistant) -> None:
|
||||
# Send coordinates + location_name: Location name has precedence
|
||||
(
|
||||
{"gps": [10, 20], "location_name": "home"},
|
||||
{"latitude": 10, "longitude": 20, "gps_accuracy": 30},
|
||||
{
|
||||
"latitude": 10,
|
||||
"longitude": 20,
|
||||
"gps_accuracy": 30,
|
||||
"in_zones": ["zone.home"],
|
||||
},
|
||||
"home",
|
||||
),
|
||||
(
|
||||
{"gps": [20, 30], "location_name": "office"},
|
||||
{"latitude": 20, "longitude": 30, "gps_accuracy": 30},
|
||||
{
|
||||
"latitude": 20,
|
||||
"longitude": 30,
|
||||
"gps_accuracy": 30,
|
||||
"in_zones": ["zone.office"],
|
||||
},
|
||||
"Office",
|
||||
),
|
||||
(
|
||||
{"gps": [30, 40], "location_name": "school"},
|
||||
{"latitude": 30, "longitude": 40, "gps_accuracy": 30},
|
||||
{
|
||||
"latitude": 30,
|
||||
"longitude": 40,
|
||||
"gps_accuracy": 30,
|
||||
"in_zones": ["zone.school"],
|
||||
},
|
||||
"School",
|
||||
),
|
||||
# Send wrong coordinates + location_name: Location name has precedence
|
||||
(
|
||||
{"gps": [10, 10], "location_name": "home"},
|
||||
{"latitude": 10, "longitude": 10, "gps_accuracy": 30},
|
||||
{"latitude": 10, "longitude": 10, "gps_accuracy": 30, "in_zones": []},
|
||||
"home",
|
||||
),
|
||||
(
|
||||
{"gps": [10, 10], "location_name": "office"},
|
||||
{"latitude": 10, "longitude": 10, "gps_accuracy": 30},
|
||||
{"latitude": 10, "longitude": 10, "gps_accuracy": 30, "in_zones": []},
|
||||
"Office",
|
||||
),
|
||||
(
|
||||
{"gps": [10, 10], "location_name": "school"},
|
||||
{"latitude": 10, "longitude": 10, "gps_accuracy": 30},
|
||||
{"latitude": 10, "longitude": 10, "gps_accuracy": 30, "in_zones": []},
|
||||
"School",
|
||||
),
|
||||
# Send location_name only
|
||||
({"location_name": "home"}, {}, "home"),
|
||||
({"location_name": "office"}, {}, "Office"),
|
||||
({"location_name": "school"}, {}, "School"),
|
||||
({"location_name": "home"}, {"in_zones": []}, "home"),
|
||||
({"location_name": "office"}, {"in_zones": []}, "Office"),
|
||||
({"location_name": "school"}, {"in_zones": []}, "School"),
|
||||
# Send coordinates only - location is determined by coordinates
|
||||
(
|
||||
{"gps": [10, 20]},
|
||||
{"latitude": 10, "longitude": 20, "gps_accuracy": 30},
|
||||
{
|
||||
"latitude": 10,
|
||||
"longitude": 20,
|
||||
"gps_accuracy": 30,
|
||||
"in_zones": ["zone.home"],
|
||||
},
|
||||
"home",
|
||||
),
|
||||
(
|
||||
{"gps": [20, 30]},
|
||||
{"latitude": 20, "longitude": 30, "gps_accuracy": 30},
|
||||
{
|
||||
"latitude": 20,
|
||||
"longitude": 30,
|
||||
"gps_accuracy": 30,
|
||||
"in_zones": ["zone.office"],
|
||||
},
|
||||
"Office",
|
||||
),
|
||||
(
|
||||
{"gps": [30, 40]},
|
||||
{"latitude": 30, "longitude": 40, "gps_accuracy": 30},
|
||||
{
|
||||
"latitude": 30,
|
||||
"longitude": 40,
|
||||
"gps_accuracy": 30,
|
||||
"in_zones": ["zone.school"],
|
||||
},
|
||||
"School",
|
||||
),
|
||||
],
|
||||
@@ -180,6 +210,7 @@ async def test_sending_location(
|
||||
"course": 6,
|
||||
"speed": 7,
|
||||
"vertical_accuracy": 8,
|
||||
"in_zones": [],
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -239,6 +239,7 @@ async def test_redact_diagnostics(
|
||||
"state": {
|
||||
"attributes": {
|
||||
"gps_accuracy": 1.5,
|
||||
"in_zones": ["zone.home"],
|
||||
"latitude": "**REDACTED**",
|
||||
"longitude": "**REDACTED**",
|
||||
"source_type": "gps",
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'NRGkick Test GPS tracker',
|
||||
'gps_accuracy': 1.5,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 47.0748,
|
||||
'longitude': 15.4376,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'REG-ZOE-50 Location',
|
||||
'in_zones': list([
|
||||
]),
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
@@ -142,6 +144,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'REG-CAPTUR-FUEL Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 48.1234567,
|
||||
'longitude': 11.1234567,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -196,6 +200,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'REG-CAPTUR_PHEV Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 48.1234567,
|
||||
'longitude': 11.1234567,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -250,6 +256,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'REG-MEG-0 Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 48.1234567,
|
||||
'longitude': 11.1234567,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -304,6 +312,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'REG-TWINGO-III Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 48.1234567,
|
||||
'longitude': 11.1234567,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -358,6 +368,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'REG-ZOE-50 Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 48.1234567,
|
||||
'longitude': 11.1234567,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Test Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': -30.222626,
|
||||
'longitude': -97.6236871,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -95,6 +97,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Test Route',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 30.2226265,
|
||||
'longitude': -97.6236871,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Test Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': -30.222626,
|
||||
'longitude': -97.6236871,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -95,6 +97,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Test Route',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 30.2226265,
|
||||
'longitude': -97.6236871,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -112,6 +116,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Test Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': -30.222626,
|
||||
'longitude': -97.6236871,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -129,6 +135,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Test Route',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 30.2226265,
|
||||
'longitude': -97.6236871,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
'friendly_name': 'Test Location',
|
||||
'gps_accuracy': 0,
|
||||
'heading': 185,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': -30.222626,
|
||||
'longitude': -97.6236871,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -97,6 +99,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Test Route',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 30.2226265,
|
||||
'longitude': -97.6236871,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
'altitude': 0,
|
||||
'friendly_name': 'Wallet',
|
||||
'gps_accuracy': 13.496111,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'is_lost': False,
|
||||
'last_lost_timestamp': datetime.datetime(1970, 1, 1, 3, 0, tzinfo=datetime.timezone.utc),
|
||||
'last_timestamp': datetime.datetime(2020, 8, 13, 0, 55, 26, tzinfo=datetime.timezone.utc),
|
||||
|
||||
@@ -103,6 +103,8 @@
|
||||
'custom_attr_1': 'custom_attr_1_value',
|
||||
'friendly_name': 'X-Wing',
|
||||
'gps_accuracy': 3.5,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': '**REDACTED**',
|
||||
'longitude': '**REDACTED**',
|
||||
'source_type': 'gps',
|
||||
@@ -397,6 +399,8 @@
|
||||
'custom_attr_1': 'custom_attr_1_value',
|
||||
'friendly_name': 'X-Wing',
|
||||
'gps_accuracy': 3.5,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': '**REDACTED**',
|
||||
'longitude': '**REDACTED**',
|
||||
'source_type': 'gps',
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
'battery_level': 88,
|
||||
'friendly_name': 'Test Pet Tracker',
|
||||
'gps_accuracy': 99,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 22.333,
|
||||
'longitude': 44.555,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Volvo EX30 Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 57.72537482589284,
|
||||
'longitude': 11.849843629550225,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -95,6 +97,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Volvo S90 Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 57.72537482589284,
|
||||
'longitude': 11.849843629550225,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -149,6 +153,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Volvo XC40 Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 57.72537482589284,
|
||||
'longitude': 11.849843629550225,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
@@ -203,6 +209,8 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Volvo XC90 Location',
|
||||
'gps_accuracy': 0,
|
||||
'in_zones': list([
|
||||
]),
|
||||
'latitude': 57.72537482589284,
|
||||
'longitude': 11.849843629550225,
|
||||
'source_type': <SourceType.GPS: 'gps'>,
|
||||
|
||||
@@ -126,8 +126,11 @@ async def test_active_zone_skips_passive_zones(hass: HomeAssistant) -> None:
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
active = zone.async_active_zone(hass, 32.880600, -117.237561)
|
||||
assert active is None
|
||||
active_zone = zone.async_active_zone(hass, 32.880600, -117.237561)
|
||||
assert active_zone is None
|
||||
active_zone, in_zones = zone.async_in_zones(hass, 32.880600, -117.237561)
|
||||
assert active_zone is None
|
||||
assert in_zones == ["zone.passive_zone"]
|
||||
|
||||
|
||||
async def test_active_zone_skips_passive_zones_2(hass: HomeAssistant) -> None:
|
||||
@@ -147,8 +150,11 @@ async def test_active_zone_skips_passive_zones_2(hass: HomeAssistant) -> None:
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
active = zone.async_active_zone(hass, 32.880700, -117.237561)
|
||||
assert active.entity_id == "zone.active_zone"
|
||||
active_zone = zone.async_active_zone(hass, 32.880700, -117.237561)
|
||||
assert active_zone.entity_id == "zone.active_zone"
|
||||
active_zone, in_zones = zone.async_in_zones(hass, 32.880600, -117.237561)
|
||||
assert active_zone.entity_id == "zone.active_zone"
|
||||
assert in_zones == ["zone.active_zone"]
|
||||
|
||||
|
||||
async def test_active_zone_prefers_smaller_zone_if_same_distance(
|
||||
@@ -178,8 +184,11 @@ async def test_active_zone_prefers_smaller_zone_if_same_distance(
|
||||
},
|
||||
)
|
||||
|
||||
active = zone.async_active_zone(hass, latitude, longitude)
|
||||
assert active.entity_id == "zone.small_zone"
|
||||
active_zone = zone.async_active_zone(hass, latitude, longitude)
|
||||
assert active_zone.entity_id == "zone.small_zone"
|
||||
active_zone, in_zones = zone.async_in_zones(hass, latitude, longitude)
|
||||
assert active_zone.entity_id == "zone.small_zone"
|
||||
assert in_zones == ["zone.small_zone", "zone.big_zone"]
|
||||
|
||||
|
||||
async def test_active_zone_prefers_smaller_zone_if_same_distance_2(
|
||||
@@ -203,8 +212,11 @@ async def test_active_zone_prefers_smaller_zone_if_same_distance_2(
|
||||
},
|
||||
)
|
||||
|
||||
active = zone.async_active_zone(hass, latitude, longitude)
|
||||
assert active.entity_id == "zone.smallest_zone"
|
||||
active_zone = zone.async_active_zone(hass, latitude, longitude)
|
||||
assert active_zone.entity_id == "zone.smallest_zone"
|
||||
active_zone, in_zones = zone.async_in_zones(hass, latitude, longitude)
|
||||
assert active_zone.entity_id == "zone.smallest_zone"
|
||||
assert in_zones == ["zone.smallest_zone"]
|
||||
|
||||
|
||||
async def test_in_zone_works_for_passive_zones(hass: HomeAssistant) -> None:
|
||||
@@ -263,11 +275,17 @@ async def test_async_active_zone_with_non_zero_radius(
|
||||
assert home_state.attributes["latitude"] == 32.87336
|
||||
assert home_state.attributes["longitude"] == -117.22743
|
||||
|
||||
active = zone.async_active_zone(hass, latitude, longitude, 5000)
|
||||
assert active.entity_id == "zone.home"
|
||||
active_zone = zone.async_active_zone(hass, latitude, longitude, 5000)
|
||||
assert active_zone.entity_id == "zone.home"
|
||||
active_zone, in_zones = zone.async_in_zones(hass, latitude, longitude, 5000)
|
||||
assert active_zone.entity_id == "zone.home"
|
||||
assert in_zones == ["zone.home", "zone.small_zone", "zone.big_zone"]
|
||||
|
||||
active = zone.async_active_zone(hass, latitude, longitude, 0)
|
||||
assert active.entity_id == "zone.small_zone"
|
||||
active_zone = zone.async_active_zone(hass, latitude, longitude, 0)
|
||||
assert active_zone.entity_id == "zone.small_zone"
|
||||
active_zone, in_zones = zone.async_in_zones(hass, latitude, longitude, 0)
|
||||
assert active_zone.entity_id == "zone.small_zone"
|
||||
assert in_zones == ["zone.small_zone", "zone.big_zone"]
|
||||
|
||||
|
||||
async def test_core_config_update(hass: HomeAssistant) -> None:
|
||||
@@ -567,6 +585,9 @@ async def test_unavailable_zone(hass: HomeAssistant) -> None:
|
||||
hass.states.async_set("zone.bla", "unavailable", {"restored": True})
|
||||
|
||||
assert zone.async_active_zone(hass, 0.0, 0.01) is None
|
||||
active_zone, in_zones = zone.async_in_zones(hass, 0.0, 0.01)
|
||||
assert active_zone is None
|
||||
assert in_zones == []
|
||||
|
||||
assert zone.in_zone(hass.states.get("zone.bla"), 0, 0) is False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user