diff --git a/homeassistant/components/teslemetry/coordinator.py b/homeassistant/components/teslemetry/coordinator.py index f861cf277992..d1b6268ea5e2 100644 --- a/homeassistant/components/teslemetry/coordinator.py +++ b/homeassistant/components/teslemetry/coordinator.py @@ -374,15 +374,6 @@ class TeslemetryEnergyHistoryCoordinator(DataUpdateCoordinator[dict[str, Any]]): return self.time_zone = zone - @override - async def _async_update_data(self) -> dict[str, Any]: - """Return the current totals; there is nothing to fetch. - - Only reached through the generic entity update service, which must not - fail on a coordinator the stream alone feeds. - """ - return self.data - def handle_stream_update(self, event: EnergyTotalsEvent) -> None: """Handle an energy_totals document from the stream.""" data: dict[str, Any] = asdict(event.totals) diff --git a/homeassistant/components/teslemetry/entity.py b/homeassistant/components/teslemetry/entity.py index 9c4c3d88b7b8..a451ef2e39c5 100644 --- a/homeassistant/components/teslemetry/entity.py +++ b/homeassistant/components/teslemetry/entity.py @@ -194,6 +194,10 @@ class TeslemetryEnergyHistoryEntity(TeslemetryPollingEntity): super().__init__(data.history_coordinator, key) + @override + async def async_update(self) -> None: + """Keep the streamed totals; the stream is their only source.""" + class TeslemetryWallConnectorEntity(TeslemetryPollingEntity): """Parent class for Teslemetry Wall Connector Entities.""" diff --git a/tests/components/teslemetry/test_sensor.py b/tests/components/teslemetry/test_sensor.py index f01a435cd587..830331bd2e14 100644 --- a/tests/components/teslemetry/test_sensor.py +++ b/tests/components/teslemetry/test_sensor.py @@ -1,7 +1,6 @@ """Test the Teslemetry sensor platform.""" from copy import deepcopy -from datetime import timedelta from unittest.mock import AsyncMock, MagicMock from freezegun.api import FrozenDateTimeFactory @@ -748,22 +747,33 @@ async def test_energy_history_time_zone_fallback( assert state.attributes["last_reset"] == "2024-09-18T00:00:00-07:00" -async def test_energy_history_update_entity_service_is_a_noop( +@pytest.mark.parametrize( + ("connected", "expected_state"), + [ + pytest.param(True, "0.036", id="connected"), + pytest.param(False, STATE_UNAVAILABLE, id="disconnected"), + ], +) +async def test_energy_history_update_entity_service( hass: HomeAssistant, - freezer: FrozenDateTimeFactory, caplog: pytest.LogCaptureFixture, + mock_add_connection_listener: MagicMock, mock_energy_totals_stream: MagicMock, + connected: bool, + expected_state: str, ) -> None: - """The generic update service keeps the streamed totals instead of failing. + """The generic update service leaves the history sensors as the stream set them. - The coordinator has nothing to fetch, so the service must not leave the - sensors unavailable on a stream that is perfectly healthy. + The stream is their only source, so the service must neither fail on a + healthy stream nor revive stale totals while it is down. """ await setup_platform(hass, [Platform.SENSOR]) await async_setup_component(hass, HOMEASSISTANT_DOMAIN, {}) mock_energy_totals_stream.send() await hass.async_block_till_done() + mock_add_connection_listener.send(connected) + await hass.async_block_till_done() await hass.services.async_call( HOMEASSISTANT_DOMAIN, @@ -771,15 +781,10 @@ async def test_energy_history_update_entity_service_is_a_noop( {ATTR_ENTITY_ID: ENERGY_HISTORY_ENTITY}, blocking=True, ) - # The coordinator debounces refresh requests, so let the deferred one land. - freezer.tick(timedelta(seconds=30)) - async_fire_time_changed(hass) - await hass.async_block_till_done() assert "NotImplementedError" not in caplog.text assert (state := hass.states.get(ENERGY_HISTORY_ENTITY)) - assert state.state == "0.036" - assert state.attributes["last_reset"] == SITE_MIDNIGHT + assert state.state == expected_state async def test_energy_history_unavailable_while_stream_disconnected(