Add Starlink device tracker (#91445)
* Fetch location data and redact in diagnostics
* Implement device tracker
* Fix failing tests
* Update starlink-grpc-core
* Update coveragerc
* Hardcode GPS source type
* Use translations
* Move DEVICE_TRACKERS a little higher in the file
* Separate status and location check try/catches
* Revert "Separate status and location check try/catches"
This reverts commit 7628ec62f6
.
pull/98682/head
parent
c526d23686
commit
8a6bde1191
|
@ -1187,6 +1187,7 @@ omit =
|
|||
homeassistant/components/starlink/binary_sensor.py
|
||||
homeassistant/components/starlink/button.py
|
||||
homeassistant/components/starlink/coordinator.py
|
||||
homeassistant/components/starlink/device_tracker.py
|
||||
homeassistant/components/starlink/sensor.py
|
||||
homeassistant/components/starlink/switch.py
|
||||
homeassistant/components/starline/__init__.py
|
||||
|
|
|
@ -11,6 +11,7 @@ from .coordinator import StarlinkUpdateCoordinator
|
|||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.BUTTON,
|
||||
Platform.DEVICE_TRACKER,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
|
|
@ -10,8 +10,10 @@ from starlink_grpc import (
|
|||
AlertDict,
|
||||
ChannelContext,
|
||||
GrpcError,
|
||||
LocationDict,
|
||||
ObstructionDict,
|
||||
StatusDict,
|
||||
location_data,
|
||||
reboot,
|
||||
set_stow_state,
|
||||
status_data,
|
||||
|
@ -28,6 +30,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||
class StarlinkData:
|
||||
"""Contains data pulled from the Starlink system."""
|
||||
|
||||
location: LocationDict
|
||||
status: StatusDict
|
||||
obstruction: ObstructionDict
|
||||
alert: AlertDict
|
||||
|
@ -53,7 +56,10 @@ class StarlinkUpdateCoordinator(DataUpdateCoordinator[StarlinkData]):
|
|||
status = await self.hass.async_add_executor_job(
|
||||
status_data, self.channel_context
|
||||
)
|
||||
return StarlinkData(*status)
|
||||
location = await self.hass.async_add_executor_job(
|
||||
location_data, self.channel_context
|
||||
)
|
||||
return StarlinkData(location, *status)
|
||||
except GrpcError as exc:
|
||||
raise UpdateFailed from exc
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
"""Contains device trackers exposed by the Starlink integration."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
|
||||
from homeassistant.components.device_tracker import SourceType, TrackerEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import EntityDescription
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import StarlinkData
|
||||
from .entity import StarlinkEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up all binary sensors for this entry."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
StarlinkDeviceTrackerEntity(coordinator, description)
|
||||
for description in DEVICE_TRACKERS
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class StarlinkDeviceTrackerEntityDescriptionMixin:
|
||||
"""Describes a Starlink device tracker."""
|
||||
|
||||
latitude_fn: Callable[[StarlinkData], float]
|
||||
longitude_fn: Callable[[StarlinkData], float]
|
||||
|
||||
|
||||
@dataclass
|
||||
class StarlinkDeviceTrackerEntityDescription(
|
||||
EntityDescription, StarlinkDeviceTrackerEntityDescriptionMixin
|
||||
):
|
||||
"""Describes a Starlink button entity."""
|
||||
|
||||
|
||||
DEVICE_TRACKERS = [
|
||||
StarlinkDeviceTrackerEntityDescription(
|
||||
key="device_location",
|
||||
translation_key="device_location",
|
||||
entity_registry_enabled_default=False,
|
||||
latitude_fn=lambda data: data.location["latitude"],
|
||||
longitude_fn=lambda data: data.location["longitude"],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class StarlinkDeviceTrackerEntity(StarlinkEntity, TrackerEntity):
|
||||
"""A TrackerEntity for Starlink devices. Handles creating unique IDs."""
|
||||
|
||||
entity_description: StarlinkDeviceTrackerEntityDescription
|
||||
|
||||
@property
|
||||
def source_type(self) -> SourceType | str:
|
||||
"""Return the source type, eg gps or router, of the device."""
|
||||
return SourceType.GPS
|
||||
|
||||
@property
|
||||
def latitude(self) -> float | None:
|
||||
"""Return latitude value of the device."""
|
||||
return self.entity_description.latitude_fn(self.coordinator.data)
|
||||
|
||||
@property
|
||||
def longitude(self) -> float | None:
|
||||
"""Return longitude value of the device."""
|
||||
return self.entity_description.longitude_fn(self.coordinator.data)
|
|
@ -10,7 +10,7 @@ from homeassistant.core import HomeAssistant
|
|||
from .const import DOMAIN
|
||||
from .coordinator import StarlinkUpdateCoordinator
|
||||
|
||||
TO_REDACT = {"id"}
|
||||
TO_REDACT = {"id", "latitude", "longitude", "altitude"}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(
|
||||
|
|
|
@ -44,6 +44,11 @@
|
|||
"name": "Unexpected location"
|
||||
}
|
||||
},
|
||||
"device_tracker": {
|
||||
"device_location": {
|
||||
"name": "Device location"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"ping": {
|
||||
"name": "Ping"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"latitude": 37.422,
|
||||
"longitude": -122.084,
|
||||
"altitude": 100
|
||||
}
|
|
@ -8,11 +8,16 @@ SETUP_ENTRY_PATCHER = patch(
|
|||
"homeassistant.components.starlink.async_setup_entry", return_value=True
|
||||
)
|
||||
|
||||
COORDINATOR_SUCCESS_PATCHER = patch(
|
||||
STATUS_DATA_SUCCESS_PATCHER = patch(
|
||||
"homeassistant.components.starlink.coordinator.status_data",
|
||||
return_value=json.loads(load_fixture("status_data_success.json", "starlink")),
|
||||
)
|
||||
|
||||
LOCATION_DATA_SUCCESS_PATCHER = patch(
|
||||
"homeassistant.components.starlink.coordinator.location_data",
|
||||
return_value=json.loads(load_fixture("location_data_success.json", "starlink")),
|
||||
)
|
||||
|
||||
DEVICE_FOUND_PATCHER = patch(
|
||||
"homeassistant.components.starlink.config_flow.get_id", return_value="some-valid-id"
|
||||
)
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
'alert_thermal_throttle': False,
|
||||
'alert_unexpected_location': False,
|
||||
}),
|
||||
'location': dict({
|
||||
'altitude': '**REDACTED**',
|
||||
'latitude': '**REDACTED**',
|
||||
'longitude': '**REDACTED**',
|
||||
}),
|
||||
'obstruction': dict({
|
||||
'raw_wedges_fraction_obstructed[]': list([
|
||||
None,
|
||||
|
|
|
@ -5,7 +5,7 @@ from homeassistant.components.starlink.const import DOMAIN
|
|||
from homeassistant.const import CONF_IP_ADDRESS
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .patchers import COORDINATOR_SUCCESS_PATCHER
|
||||
from .patchers import LOCATION_DATA_SUCCESS_PATCHER, STATUS_DATA_SUCCESS_PATCHER
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
|
@ -23,7 +23,7 @@ async def test_diagnostics(
|
|||
data={CONF_IP_ADDRESS: "1.2.3.4:0000"},
|
||||
)
|
||||
|
||||
with COORDINATOR_SUCCESS_PATCHER:
|
||||
with STATUS_DATA_SUCCESS_PATCHER, LOCATION_DATA_SUCCESS_PATCHER:
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
|
|
@ -4,7 +4,7 @@ from homeassistant.config_entries import ConfigEntryState
|
|||
from homeassistant.const import CONF_IP_ADDRESS
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .patchers import COORDINATOR_SUCCESS_PATCHER
|
||||
from .patchers import LOCATION_DATA_SUCCESS_PATCHER, STATUS_DATA_SUCCESS_PATCHER
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
@ -16,7 +16,7 @@ async def test_successful_entry(hass: HomeAssistant) -> None:
|
|||
data={CONF_IP_ADDRESS: "1.2.3.4:0000"},
|
||||
)
|
||||
|
||||
with COORDINATOR_SUCCESS_PATCHER:
|
||||
with STATUS_DATA_SUCCESS_PATCHER, LOCATION_DATA_SUCCESS_PATCHER:
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
@ -33,7 +33,7 @@ async def test_unload_entry(hass: HomeAssistant) -> None:
|
|||
data={CONF_IP_ADDRESS: "1.2.3.4:0000"},
|
||||
)
|
||||
|
||||
with COORDINATOR_SUCCESS_PATCHER:
|
||||
with STATUS_DATA_SUCCESS_PATCHER, LOCATION_DATA_SUCCESS_PATCHER:
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
|
Loading…
Reference in New Issue