Change Ambient Network timestamp updates (#116941)

This commit is contained in:
Thomas Kistler 2024-06-21 07:36:53 -07:00 committed by GitHub
parent f353b3fa54
commit 180c244a78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 1954 additions and 53 deletions

View file

@ -12,6 +12,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_MAC
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util
from .const import API_LAST_DATA, DOMAIN, LOGGER
from .helper import get_station_name
@ -24,6 +25,7 @@ class AmbientNetworkDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]])
config_entry: ConfigEntry
station_name: str
last_measured: datetime | None = None
def __init__(self, hass: HomeAssistant, api: OpenAPI) -> None:
"""Initialize the coordinator."""
@ -47,19 +49,13 @@ class AmbientNetworkDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]])
f"Station '{self.config_entry.title}' did not report any data"
)
# Eliminate data if the station hasn't been updated for a while.
if (created_at := last_data.get("created_at")) is None:
raise UpdateFailed(
f"Station '{self.config_entry.title}' did not report a time stamp"
)
# Eliminate data that has been generated more than an hour ago. The station is
# probably offline.
if int(created_at / 1000) < int(
(datetime.now() - timedelta(hours=1)).timestamp()
):
raise UpdateFailed(
f"Station '{self.config_entry.title}' reported stale data"
# Some stations do not report a "created_at" or "dateutc".
# See https://github.com/home-assistant/core/issues/116917
if (ts := last_data.get("created_at")) is not None or (
ts := last_data.get("dateutc")
) is not None:
self.last_measured = datetime.fromtimestamp(
ts / 1000, tz=dt_util.DEFAULT_TIME_ZONE
)
return cast(dict[str, Any], last_data)

View file

@ -299,12 +299,10 @@ class AmbientNetworkSensor(AmbientNetworkEntity, SensorEntity):
mac_address: str,
) -> None:
"""Initialize a sensor object."""
super().__init__(coordinator, description, mac_address)
def _update_attrs(self) -> None:
"""Update sensor attributes."""
value = self.coordinator.data.get(self.entity_description.key)
# Treatments for special units.
@ -315,3 +313,8 @@ class AmbientNetworkSensor(AmbientNetworkEntity, SensorEntity):
self._attr_available = value is not None
self._attr_native_value = value
if self.coordinator.last_measured is not None:
self._attr_extra_state_attributes = {
"last_measured": self.coordinator.last_measured
}

View file

@ -3,5 +3,8 @@
"macAddress": "BB:BB:BB:BB:BB:BB",
"info": {
"name": "Station B"
},
"lastData": {
"tempf": 82.9
}
}

View file

@ -3,7 +3,7 @@
"macAddress": "CC:CC:CC:CC:CC:CC",
"lastData": {
"stationtype": "AMBWeatherPro_V5.0.6",
"dateutc": 1699474320000,
"dateutc": 1717687683000,
"tempf": 82.9,
"dewPoint": 82.0,
"feelsLike": 85.0,

View file

@ -0,0 +1,30 @@
{
"_id": "dddddddddddddddddddddddddddddddd",
"macAddress": "DD:DD:DD:DD:DD:DD",
"lastData": {
"stationtype": "AMBWeatherPro_V5.0.6",
"tempf": 82.9,
"dewPoint": 82.0,
"feelsLike": 85.0,
"humidity": 60,
"windspeedmph": 8.72,
"windgustmph": 9.17,
"maxdailygust": 22.82,
"winddir": 11,
"uv": 0,
"solarradiation": 37.64,
"hourlyrainin": 0,
"dailyrainin": 0,
"weeklyrainin": 0,
"monthlyrainin": 0,
"totalrainin": 26.402,
"baromrelin": 29.586,
"baromabsin": 28.869,
"batt_co2": 1,
"type": "weather-data",
"tz": "America/Chicago"
},
"info": {
"name": "Station D"
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
"""Test Ambient Weather Network sensors."""
from datetime import datetime, timedelta
from unittest.mock import patch
from unittest.mock import AsyncMock, patch
from aioambient import OpenAPI
from aioambient.errors import RequestError
@ -9,6 +9,7 @@ from freezegun import freeze_time
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
@ -17,65 +18,47 @@ from .conftest import setup_platform
from tests.common import async_fire_time_changed, snapshot_platform
@freeze_time("2023-11-08")
@pytest.mark.parametrize("config_entry", ["AA:AA:AA:AA:AA:AA"], indirect=True)
@freeze_time("2023-11-9")
@pytest.mark.parametrize(
"config_entry",
["AA:AA:AA:AA:AA:AA", "CC:CC:CC:CC:CC:CC", "DD:DD:DD:DD:DD:DD"],
indirect=True,
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensors(
hass: HomeAssistant,
open_api: OpenAPI,
aioambient,
config_entry,
aioambient: AsyncMock,
config_entry: ConfigEntry,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test all sensors under normal operation."""
await setup_platform(True, hass, config_entry)
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
@freeze_time("2023-11-09")
@pytest.mark.parametrize("config_entry", ["AA:AA:AA:AA:AA:AA"], indirect=True)
async def test_sensors_with_stale_data(
hass: HomeAssistant, open_api: OpenAPI, aioambient, config_entry
) -> None:
"""Test that the sensors are not populated if the data is stale."""
await setup_platform(False, hass, config_entry)
sensor = hass.states.get("sensor.station_a_absolute_pressure")
assert sensor is None
@freeze_time("2023-11-08")
@pytest.mark.parametrize("config_entry", ["BB:BB:BB:BB:BB:BB"], indirect=True)
async def test_sensors_with_no_data(
hass: HomeAssistant, open_api: OpenAPI, aioambient, config_entry
hass: HomeAssistant,
open_api: OpenAPI,
aioambient: AsyncMock,
config_entry: ConfigEntry,
) -> None:
"""Test that the sensors are not populated if the last data is absent."""
await setup_platform(False, hass, config_entry)
await setup_platform(True, hass, config_entry)
sensor = hass.states.get("sensor.station_b_absolute_pressure")
assert sensor is None
@freeze_time("2023-11-08")
@pytest.mark.parametrize("config_entry", ["CC:CC:CC:CC:CC:CC"], indirect=True)
async def test_sensors_with_no_update_time(
hass: HomeAssistant, open_api: OpenAPI, aioambient, config_entry
) -> None:
"""Test that the sensors are not populated if the update time is missing."""
await setup_platform(False, hass, config_entry)
sensor = hass.states.get("sensor.station_c_absolute_pressure")
assert sensor is None
sensor = hass.states.get("sensor.station_b_temperature")
assert sensor is not None
assert "last_measured" not in sensor.attributes
@pytest.mark.parametrize("config_entry", ["AA:AA:AA:AA:AA:AA"], indirect=True)
async def test_sensors_disappearing(
hass: HomeAssistant,
open_api: OpenAPI,
aioambient,
config_entry,
aioambient: AsyncMock,
config_entry: ConfigEntry,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test that we log errors properly."""