Handle Starlink alerts the dish does not report (#181687)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Armin Azar
2026-09-13 11:11:50 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent fa9c573fc4
commit 32343f08b8
2 changed files with 106 additions and 9 deletions
@@ -52,12 +52,12 @@ BINARY_SENSORS = [
StarlinkBinarySensorEntityDescription(
key="update",
device_class=BinarySensorDeviceClass.UPDATE,
value_fn=lambda data: data.alert["alert_install_pending"],
value_fn=lambda data: data.alert.get("alert_install_pending"),
),
StarlinkBinarySensorEntityDescription(
key="roaming",
translation_key="roaming",
value_fn=lambda data: data.alert["alert_roaming"],
value_fn=lambda data: data.alert.get("alert_roaming"),
),
StarlinkBinarySensorEntityDescription(
key="currently_obstructed",
@@ -70,48 +70,48 @@ BINARY_SENSORS = [
key="heating",
translation_key="heating",
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.alert["alert_is_heating"],
value_fn=lambda data: data.alert.get("alert_is_heating"),
),
StarlinkBinarySensorEntityDescription(
key="power_save_idle",
translation_key="power_save_idle",
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.alert["alert_is_power_save_idle"],
value_fn=lambda data: data.alert.get("alert_is_power_save_idle"),
),
StarlinkBinarySensorEntityDescription(
key="mast_near_vertical",
translation_key="mast_near_vertical",
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.alert["alert_mast_not_near_vertical"],
value_fn=lambda data: data.alert.get("alert_mast_not_near_vertical"),
),
StarlinkBinarySensorEntityDescription(
key="motors_stuck",
translation_key="motors_stuck",
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.alert["alert_motors_stuck"],
value_fn=lambda data: data.alert.get("alert_motors_stuck"),
),
StarlinkBinarySensorEntityDescription(
key="slow_ethernet",
translation_key="slow_ethernet",
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.alert["alert_slow_ethernet_speeds"],
value_fn=lambda data: data.alert.get("alert_slow_ethernet_speeds"),
),
StarlinkBinarySensorEntityDescription(
key="thermal_throttle",
translation_key="thermal_throttle",
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.alert["alert_thermal_throttle"],
value_fn=lambda data: data.alert.get("alert_thermal_throttle"),
),
StarlinkBinarySensorEntityDescription(
key="unexpected_location",
translation_key="unexpected_location",
device_class=BinarySensorDeviceClass.PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda data: data.alert["alert_unexpected_location"],
value_fn=lambda data: data.alert.get("alert_unexpected_location"),
),
StarlinkBinarySensorEntityDescription(
key="connection",
@@ -0,0 +1,97 @@
"""Tests Starlink binary sensors."""
from copy import deepcopy
from unittest.mock import patch
import pytest
from homeassistant.components.starlink.const import DOMAIN
from homeassistant.const import CONF_IP_ADDRESS, STATE_OFF, STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.util.json import JsonArrayType
from .patchers import (
HISTORY_STATS_SUCCESS_PATCHER,
LOCATION_DATA_SUCCESS_PATCHER,
SLEEP_DATA_SUCCESS_PATCHER,
STATUS_DATA_FIXTURE,
STATUS_DATA_TARGET,
)
from tests.common import MockConfigEntry
# Alerts that a dish which does not support roaming stops reporting. The
# library builds the alert dict from the fields the dish actually sends, so
# these keys are simply absent instead of being False.
MISSING_ALERTS = (
"alert_roaming",
"alert_unexpected_location",
"alert_is_power_save_idle",
)
async def setup_integration(
hass: HomeAssistant, status_data: JsonArrayType
) -> MockConfigEntry:
"""Set up the Starlink integration with the given status data."""
entry = MockConfigEntry(domain=DOMAIN, data={CONF_IP_ADDRESS: "1.2.3.4:0000"})
with (
LOCATION_DATA_SUCCESS_PATCHER,
SLEEP_DATA_SUCCESS_PATCHER,
HISTORY_STATS_SUCCESS_PATCHER,
patch(STATUS_DATA_TARGET, return_value=status_data),
):
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
return entry
@pytest.mark.parametrize(
"entity_id",
[
"binary_sensor.starlink_roaming_mode",
"binary_sensor.starlink_unexpected_location",
"binary_sensor.starlink_sleep",
],
)
async def test_alert_reported_by_dish(hass: HomeAssistant, entity_id: str) -> None:
"""Test that an alert the dish reports is used as the state."""
await setup_integration(hass, deepcopy(STATUS_DATA_FIXTURE))
assert hass.states.get(entity_id).state == STATE_OFF
@pytest.mark.parametrize(
"entity_id",
[
"binary_sensor.starlink_roaming_mode",
"binary_sensor.starlink_unexpected_location",
"binary_sensor.starlink_sleep",
],
)
async def test_alert_not_reported_by_dish(hass: HomeAssistant, entity_id: str) -> None:
"""Test that an alert the dish omits is unknown instead of raising."""
status_data = deepcopy(STATUS_DATA_FIXTURE)
for alert in MISSING_ALERTS:
del status_data[2][alert]
await setup_integration(hass, status_data)
assert hass.states.get(entity_id).state == STATE_UNKNOWN
async def test_remaining_alerts_unaffected(hass: HomeAssistant) -> None:
"""Test that alerts the dish still reports keep working."""
status_data = deepcopy(STATUS_DATA_FIXTURE)
for alert in MISSING_ALERTS:
del status_data[2][alert]
status_data[2]["alert_motors_stuck"] = True
await setup_integration(hass, status_data)
assert hass.states.get("binary_sensor.starlink_motors_stuck").state == "on"
assert hass.states.get("binary_sensor.starlink_thermal_throttle").state == STATE_OFF