diff --git a/homeassistant/components/starlink/binary_sensor.py b/homeassistant/components/starlink/binary_sensor.py index b9b0633aa710..fcd5e05eef1f 100644 --- a/homeassistant/components/starlink/binary_sensor.py +++ b/homeassistant/components/starlink/binary_sensor.py @@ -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", diff --git a/tests/components/starlink/test_binary_sensor.py b/tests/components/starlink/test_binary_sensor.py new file mode 100644 index 000000000000..c048a9552b67 --- /dev/null +++ b/tests/components/starlink/test_binary_sensor.py @@ -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