diff --git a/homeassistant/components/starlink/coordinator.py b/homeassistant/components/starlink/coordinator.py index a891941fb8e..81ee56db3b4 100644 --- a/homeassistant/components/starlink/coordinator.py +++ b/homeassistant/components/starlink/coordinator.py @@ -14,8 +14,10 @@ from starlink_grpc import ( GrpcError, LocationDict, ObstructionDict, + PowerDict, StatusDict, get_sleep_config, + history_stats, location_data, reboot, set_sleep_config, @@ -39,6 +41,7 @@ class StarlinkData: status: StatusDict obstruction: ObstructionDict alert: AlertDict + consumption: PowerDict class StarlinkUpdateCoordinator(DataUpdateCoordinator[StarlinkData]): @@ -58,10 +61,11 @@ class StarlinkUpdateCoordinator(DataUpdateCoordinator[StarlinkData]): def _get_starlink_data(self) -> StarlinkData: """Retrieve Starlink data.""" channel_context = self.channel_context - status = status_data(channel_context) location = location_data(channel_context) sleep = get_sleep_config(channel_context) - return StarlinkData(location, sleep, *status) + status, obstruction, alert = status_data(channel_context) + statistics = history_stats(parse_samples=-1, context=channel_context) + return StarlinkData(location, sleep, status, obstruction, alert, statistics[-1]) async def _async_update_data(self) -> StarlinkData: async with asyncio.timeout(4): diff --git a/homeassistant/components/starlink/sensor.py b/homeassistant/components/starlink/sensor.py index 21f2400022c..4b33a7f4337 100644 --- a/homeassistant/components/starlink/sensor.py +++ b/homeassistant/components/starlink/sensor.py @@ -18,6 +18,8 @@ from homeassistant.const import ( PERCENTAGE, EntityCategory, UnitOfDataRate, + UnitOfEnergy, + UnitOfPower, UnitOfTime, ) from homeassistant.core import HomeAssistant @@ -120,4 +122,18 @@ SENSORS: tuple[StarlinkSensorEntityDescription, ...] = ( native_unit_of_measurement=PERCENTAGE, value_fn=lambda data: data.status["pop_ping_drop_rate"] * 100, ), + StarlinkSensorEntityDescription( + key="power", + device_class=SensorDeviceClass.POWER, + state_class=SensorStateClass.MEASUREMENT, + native_unit_of_measurement=UnitOfPower.WATT, + value_fn=lambda data: data.consumption["latest_power"], + ), + StarlinkSensorEntityDescription( + key="energy", + device_class=SensorDeviceClass.ENERGY, + state_class=SensorStateClass.TOTAL_INCREASING, + native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, + value_fn=lambda data: data.consumption["total_energy"], + ), ) diff --git a/tests/components/starlink/fixtures/history_stats_success.json b/tests/components/starlink/fixtures/history_stats_success.json new file mode 100644 index 00000000000..5a228dd34af --- /dev/null +++ b/tests/components/starlink/fixtures/history_stats_success.json @@ -0,0 +1,112 @@ +[ + { + "samples": 900, + "end_counter": 119395 + }, + { + "total_ping_drop": 2.4592087380588055, + "count_full_ping_drop": 0, + "count_obstructed": 0, + "total_obstructed_ping_drop": 0, + "count_full_obstructed_ping_drop": 0, + "count_unscheduled": 0, + "total_unscheduled_ping_drop": 0, + "count_full_unscheduled_ping_drop": 0 + }, + { + "init_run_fragment": 0, + "final_run_fragment": 0, + "run_seconds[1,]": [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ], + "run_minutes[1,]": [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + }, + { + "mean_all_ping_latency": 31.55747121333472, + "deciles_all_ping_latency[]": [ + 21.005102157592773, 22.67989158630371, 25.310760498046875, + 26.85667610168457, 27.947458267211914, 29.192155838012695, + 31.26323890686035, 34.226768493652344, 38.54373550415039, + 42.308048248291016, 60.11151885986328 + ], + "mean_full_ping_latency": 31.526783029284427, + "deciles_full_ping_latency[]": [ + 21.070240020751953, 22.841461181640625, 25.34041976928711, + 26.908039093017578, 27.947458267211914, 29.135879516601562, + 31.122955322265625, 34.1280403137207, 38.49388122558594, + 42.308048248291016, 60.11151885986328 + ], + "stdev_full_ping_latency": 7.8141330200011785 + }, + { + "load_bucket_samples[]": [738, 24, 39, 55, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "load_bucket_min_latency[]": [ + 21.070240020751953, + 21.35713768005371, + 21.156545639038086, + 24.763751983642578, + 24.7109317779541, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ], + "load_bucket_median_latency[]": [ + 29.2450590133667, + 27.031108856201172, + 25.726211547851562, + 31.845806121826172, + 28.919479370117188, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ], + "load_bucket_max_latency[]": [ + 60.11151885986328, + 40.572628021240234, + 48.063961029052734, + 53.505126953125, + 38.7435302734375, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ] + }, + { + "download_usage": 72504227, + "upload_usage": 5719755 + }, + { + "latest_power": 27.54502296447754, + "mean_power": 31.449254739549424, + "min_power": 21.826229095458984, + "max_power": 41.71160888671875, + "total_energy": 0.007862313684887356 + } +] diff --git a/tests/components/starlink/patchers.py b/tests/components/starlink/patchers.py index f8179f07bed..08e82548ef8 100644 --- a/tests/components/starlink/patchers.py +++ b/tests/components/starlink/patchers.py @@ -24,6 +24,11 @@ SLEEP_DATA_SUCCESS_PATCHER = patch( return_value=json.loads(load_fixture("sleep_data_success.json", "starlink")), ) +HISTORY_STATS_SUCCESS_PATCHER = patch( + "homeassistant.components.starlink.coordinator.history_stats", + return_value=json.loads(load_fixture("history_stats_success.json", "starlink")), +) + DEVICE_FOUND_PATCHER = patch( "homeassistant.components.starlink.config_flow.get_id", return_value="some-valid-id" ) diff --git a/tests/components/starlink/snapshots/test_diagnostics.ambr b/tests/components/starlink/snapshots/test_diagnostics.ambr index 4c85ad84ca7..c0b1b93085b 100644 --- a/tests/components/starlink/snapshots/test_diagnostics.ambr +++ b/tests/components/starlink/snapshots/test_diagnostics.ambr @@ -16,6 +16,13 @@ 'alert_thermal_throttle': False, 'alert_unexpected_location': False, }), + 'consumption': dict({ + 'latest_power': 27.54502296447754, + 'max_power': 41.71160888671875, + 'mean_power': 31.449254739549424, + 'min_power': 21.826229095458984, + 'total_energy': 0.007862313684887356, + }), 'location': dict({ 'altitude': '**REDACTED**', 'latitude': '**REDACTED**', diff --git a/tests/components/starlink/test_diagnostics.py b/tests/components/starlink/test_diagnostics.py index c5876e5e9f2..cd36dd0367e 100644 --- a/tests/components/starlink/test_diagnostics.py +++ b/tests/components/starlink/test_diagnostics.py @@ -7,6 +7,7 @@ from homeassistant.const import CONF_IP_ADDRESS from homeassistant.core import HomeAssistant from .patchers import ( + HISTORY_STATS_SUCCESS_PATCHER, LOCATION_DATA_SUCCESS_PATCHER, SLEEP_DATA_SUCCESS_PATCHER, STATUS_DATA_SUCCESS_PATCHER, @@ -32,6 +33,7 @@ async def test_diagnostics( STATUS_DATA_SUCCESS_PATCHER, LOCATION_DATA_SUCCESS_PATCHER, SLEEP_DATA_SUCCESS_PATCHER, + HISTORY_STATS_SUCCESS_PATCHER, ): entry.add_to_hass(hass) diff --git a/tests/components/starlink/test_init.py b/tests/components/starlink/test_init.py index 62a1ee41236..7e04c21562a 100644 --- a/tests/components/starlink/test_init.py +++ b/tests/components/starlink/test_init.py @@ -6,6 +6,7 @@ from homeassistant.const import CONF_IP_ADDRESS from homeassistant.core import HomeAssistant from .patchers import ( + HISTORY_STATS_SUCCESS_PATCHER, LOCATION_DATA_SUCCESS_PATCHER, SLEEP_DATA_SUCCESS_PATCHER, STATUS_DATA_SUCCESS_PATCHER, @@ -25,6 +26,7 @@ async def test_successful_entry(hass: HomeAssistant) -> None: STATUS_DATA_SUCCESS_PATCHER, LOCATION_DATA_SUCCESS_PATCHER, SLEEP_DATA_SUCCESS_PATCHER, + HISTORY_STATS_SUCCESS_PATCHER, ): entry.add_to_hass(hass) @@ -46,6 +48,7 @@ async def test_unload_entry(hass: HomeAssistant) -> None: STATUS_DATA_SUCCESS_PATCHER, LOCATION_DATA_SUCCESS_PATCHER, SLEEP_DATA_SUCCESS_PATCHER, + HISTORY_STATS_SUCCESS_PATCHER, ): entry.add_to_hass(hass)