Add forecasts to MetOffice integration (#50876)
* MetOfficeData now retrieves both 3-hourly and daily data (full forecast data, as well as "now" snapshot) on each update * Bump datapoint API up to latest version * Create 2 sets of sensors - one of each set for 3-hourly and for daily data (same ones initially enabled, for now) * Create two entities (one each for 3-hourly and daily data) and also add in the forecast data for each dataset * Testing changes to accommodate now having two sets of everything for 3-hourly and daily update data * Removed unused import (reported by flake8) * As per conversation with @MatthewFlamm leave the 3-hourly entity's unique_id unchanged (although the display name is changed) * Make some improvements based on reviews Make some improvements and fix up the formatting/linting failures. * Make some improvements based on reviews Make some improvements and fix up the formatting/linting failures. * Added more test coverage * import asyncio * Try to fix test * Rewrote everything using CoordinatorEntity * Fixed config flow * Fixed lint errors Co-authored-by: MrHarcombe <ian.harcombe@gmail.com> Co-authored-by: Henco Appel <hencoappel+github@gmail.com>
This commit is contained in:
parent
23339cff95
commit
2d1744c573
14 changed files with 659 additions and 236 deletions
|
@ -10,16 +10,21 @@ from homeassistant.const import (
|
|||
TEMP_CELSIUS,
|
||||
UV_INDEX,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import (
|
||||
ATTRIBUTION,
|
||||
CONDITION_CLASSES,
|
||||
DOMAIN,
|
||||
METOFFICE_COORDINATOR,
|
||||
METOFFICE_DATA,
|
||||
METOFFICE_COORDINATES,
|
||||
METOFFICE_DAILY_COORDINATOR,
|
||||
METOFFICE_HOURLY_COORDINATOR,
|
||||
METOFFICE_NAME,
|
||||
MODE_3HOURLY_LABEL,
|
||||
MODE_DAILY,
|
||||
MODE_DAILY_LABEL,
|
||||
VISIBILITY_CLASSES,
|
||||
VISIBILITY_DISTANCE_CLASSES,
|
||||
)
|
||||
|
@ -85,28 +90,40 @@ async def async_setup_entry(
|
|||
|
||||
async_add_entities(
|
||||
[
|
||||
MetOfficeCurrentSensor(entry.data, hass_data, sensor_type)
|
||||
MetOfficeCurrentSensor(
|
||||
hass_data[METOFFICE_HOURLY_COORDINATOR], hass_data, True, sensor_type
|
||||
)
|
||||
for sensor_type in SENSOR_TYPES
|
||||
]
|
||||
+ [
|
||||
MetOfficeCurrentSensor(
|
||||
hass_data[METOFFICE_DAILY_COORDINATOR], hass_data, False, sensor_type
|
||||
)
|
||||
for sensor_type in SENSOR_TYPES
|
||||
],
|
||||
False,
|
||||
)
|
||||
|
||||
|
||||
class MetOfficeCurrentSensor(SensorEntity):
|
||||
class MetOfficeCurrentSensor(CoordinatorEntity, SensorEntity):
|
||||
"""Implementation of a Met Office current weather condition sensor."""
|
||||
|
||||
def __init__(self, entry_data, hass_data, sensor_type):
|
||||
def __init__(self, coordinator, hass_data, use_3hourly, sensor_type):
|
||||
"""Initialize the sensor."""
|
||||
self._data = hass_data[METOFFICE_DATA]
|
||||
self._coordinator = hass_data[METOFFICE_COORDINATOR]
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._type = sensor_type
|
||||
self._name = f"{hass_data[METOFFICE_NAME]} {SENSOR_TYPES[self._type][0]}"
|
||||
self._unique_id = f"{SENSOR_TYPES[self._type][0]}_{self._data.latitude}_{self._data.longitude}"
|
||||
mode_label = MODE_3HOURLY_LABEL if use_3hourly else MODE_DAILY_LABEL
|
||||
self._name = (
|
||||
f"{hass_data[METOFFICE_NAME]} {SENSOR_TYPES[self._type][0]} {mode_label}"
|
||||
)
|
||||
self._unique_id = (
|
||||
f"{SENSOR_TYPES[self._type][0]}_{hass_data[METOFFICE_COORDINATES]}"
|
||||
)
|
||||
if not use_3hourly:
|
||||
self._unique_id = f"{self._unique_id}_{MODE_DAILY}"
|
||||
|
||||
self.metoffice_site_id = None
|
||||
self.metoffice_site_name = None
|
||||
self.metoffice_now = None
|
||||
self.use_3hourly = use_3hourly
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -124,22 +141,26 @@ class MetOfficeCurrentSensor(SensorEntity):
|
|||
value = None
|
||||
|
||||
if self._type == "visibility_distance" and hasattr(
|
||||
self.metoffice_now, "visibility"
|
||||
self.coordinator.data.now, "visibility"
|
||||
):
|
||||
value = VISIBILITY_DISTANCE_CLASSES.get(self.metoffice_now.visibility.value)
|
||||
value = VISIBILITY_DISTANCE_CLASSES.get(
|
||||
self.coordinator.data.now.visibility.value
|
||||
)
|
||||
|
||||
if self._type == "visibility" and hasattr(self.metoffice_now, "visibility"):
|
||||
value = VISIBILITY_CLASSES.get(self.metoffice_now.visibility.value)
|
||||
if self._type == "visibility" and hasattr(
|
||||
self.coordinator.data.now, "visibility"
|
||||
):
|
||||
value = VISIBILITY_CLASSES.get(self.coordinator.data.now.visibility.value)
|
||||
|
||||
elif self._type == "weather" and hasattr(self.metoffice_now, self._type):
|
||||
elif self._type == "weather" and hasattr(self.coordinator.data.now, self._type):
|
||||
value = [
|
||||
k
|
||||
for k, v in CONDITION_CLASSES.items()
|
||||
if self.metoffice_now.weather.value in v
|
||||
if self.coordinator.data.now.weather.value in v
|
||||
][0]
|
||||
|
||||
elif hasattr(self.metoffice_now, self._type):
|
||||
value = getattr(self.metoffice_now, self._type)
|
||||
elif hasattr(self.coordinator.data.now, self._type):
|
||||
value = getattr(self.coordinator.data.now, self._type)
|
||||
|
||||
if not isinstance(value, int):
|
||||
value = value.value
|
||||
|
@ -175,44 +196,13 @@ class MetOfficeCurrentSensor(SensorEntity):
|
|||
"""Return the state attributes of the device."""
|
||||
return {
|
||||
ATTR_ATTRIBUTION: ATTRIBUTION,
|
||||
ATTR_LAST_UPDATE: self.metoffice_now.date if self.metoffice_now else None,
|
||||
ATTR_LAST_UPDATE: self.coordinator.data.now.date,
|
||||
ATTR_SENSOR_ID: self._type,
|
||||
ATTR_SITE_ID: self.metoffice_site_id if self.metoffice_site_id else None,
|
||||
ATTR_SITE_NAME: self.metoffice_site_name
|
||||
if self.metoffice_site_name
|
||||
else None,
|
||||
ATTR_SITE_ID: self.coordinator.data.site.id,
|
||||
ATTR_SITE_NAME: self.coordinator.data.site.name,
|
||||
}
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Set up a listener and load data."""
|
||||
self.async_on_remove(
|
||||
self._coordinator.async_add_listener(self._update_callback)
|
||||
)
|
||||
self._update_callback()
|
||||
|
||||
async def async_update(self):
|
||||
"""Schedule a custom update via the common entity update service."""
|
||||
await self._coordinator.async_request_refresh()
|
||||
|
||||
@callback
|
||||
def _update_callback(self) -> None:
|
||||
"""Load data from integration."""
|
||||
self.metoffice_site_id = self._data.site_id
|
||||
self.metoffice_site_name = self._data.site_name
|
||||
self.metoffice_now = self._data.now
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Entities do not individually poll."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def entity_registry_enabled_default(self) -> bool:
|
||||
"""Return if the entity should be enabled when first added to the entity registry."""
|
||||
return SENSOR_TYPES[self._type][4]
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return if state is available."""
|
||||
return self.metoffice_site_id is not None and self.metoffice_now is not None
|
||||
return SENSOR_TYPES[self._type][4] and self.use_3hourly
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue