Use EntityDescription - nws (#53523)
* Use EntityDescription - nws * Bugfix * Bugfix 2pull/53534/head
parent
6376b4be5c
commit
3e08e50050
|
@ -1,9 +1,10 @@
|
|||
"""Constants for National Weather Service Integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from typing import NamedTuple
|
||||
|
||||
from homeassistant.components.sensor import SensorEntityDescription
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLOUDY,
|
||||
ATTR_CONDITION_EXCEPTIONAL,
|
||||
|
@ -99,92 +100,100 @@ OBSERVATION_VALID_TIME = timedelta(minutes=20)
|
|||
FORECAST_VALID_TIME = timedelta(minutes=45)
|
||||
|
||||
|
||||
class NWSSensorMetadata(NamedTuple):
|
||||
"""Sensor metadata for an individual NWS sensor."""
|
||||
@dataclass
|
||||
class NWSSensorEntityDescription(SensorEntityDescription):
|
||||
"""Class describing NWSSensor entities."""
|
||||
|
||||
label: str
|
||||
icon: str | None
|
||||
device_class: str | None
|
||||
unit: str
|
||||
unit_convert: str
|
||||
unit_convert: str | None = None
|
||||
|
||||
|
||||
SENSOR_TYPES: dict[str, NWSSensorMetadata] = {
|
||||
"dewpoint": NWSSensorMetadata(
|
||||
"Dew Point",
|
||||
SENSOR_TYPES: tuple[NWSSensorEntityDescription, ...] = (
|
||||
NWSSensorEntityDescription(
|
||||
key="dewpoint",
|
||||
name="Dew Point",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
unit=TEMP_CELSIUS,
|
||||
unit_of_measurement=TEMP_CELSIUS,
|
||||
unit_convert=TEMP_CELSIUS,
|
||||
),
|
||||
"temperature": NWSSensorMetadata(
|
||||
"Temperature",
|
||||
NWSSensorEntityDescription(
|
||||
key="temperature",
|
||||
name="Temperature",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
unit=TEMP_CELSIUS,
|
||||
unit_of_measurement=TEMP_CELSIUS,
|
||||
unit_convert=TEMP_CELSIUS,
|
||||
),
|
||||
"windChill": NWSSensorMetadata(
|
||||
"Wind Chill",
|
||||
NWSSensorEntityDescription(
|
||||
key="windChill",
|
||||
name="Wind Chill",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
unit=TEMP_CELSIUS,
|
||||
unit_of_measurement=TEMP_CELSIUS,
|
||||
unit_convert=TEMP_CELSIUS,
|
||||
),
|
||||
"heatIndex": NWSSensorMetadata(
|
||||
"Heat Index",
|
||||
NWSSensorEntityDescription(
|
||||
key="heatIndex",
|
||||
name="Heat Index",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
unit=TEMP_CELSIUS,
|
||||
unit_of_measurement=TEMP_CELSIUS,
|
||||
unit_convert=TEMP_CELSIUS,
|
||||
),
|
||||
"relativeHumidity": NWSSensorMetadata(
|
||||
"Relative Humidity",
|
||||
NWSSensorEntityDescription(
|
||||
key="relativeHumidity",
|
||||
name="Relative Humidity",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
unit=PERCENTAGE,
|
||||
unit_of_measurement=PERCENTAGE,
|
||||
unit_convert=PERCENTAGE,
|
||||
),
|
||||
"windSpeed": NWSSensorMetadata(
|
||||
"Wind Speed",
|
||||
NWSSensorEntityDescription(
|
||||
key="windSpeed",
|
||||
name="Wind Speed",
|
||||
icon="mdi:weather-windy",
|
||||
device_class=None,
|
||||
unit=SPEED_KILOMETERS_PER_HOUR,
|
||||
unit_of_measurement=SPEED_KILOMETERS_PER_HOUR,
|
||||
unit_convert=SPEED_MILES_PER_HOUR,
|
||||
),
|
||||
"windGust": NWSSensorMetadata(
|
||||
"Wind Gust",
|
||||
NWSSensorEntityDescription(
|
||||
key="windGust",
|
||||
name="Wind Gust",
|
||||
icon="mdi:weather-windy",
|
||||
device_class=None,
|
||||
unit=SPEED_KILOMETERS_PER_HOUR,
|
||||
unit_of_measurement=SPEED_KILOMETERS_PER_HOUR,
|
||||
unit_convert=SPEED_MILES_PER_HOUR,
|
||||
),
|
||||
"windDirection": NWSSensorMetadata(
|
||||
"Wind Direction",
|
||||
NWSSensorEntityDescription(
|
||||
key="windDirection",
|
||||
name="Wind Direction",
|
||||
icon="mdi:compass-rose",
|
||||
device_class=None,
|
||||
unit=DEGREE,
|
||||
unit_of_measurement=DEGREE,
|
||||
unit_convert=DEGREE,
|
||||
),
|
||||
"barometricPressure": NWSSensorMetadata(
|
||||
"Barometric Pressure",
|
||||
NWSSensorEntityDescription(
|
||||
key="barometricPressure",
|
||||
name="Barometric Pressure",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
unit=PRESSURE_PA,
|
||||
unit_of_measurement=PRESSURE_PA,
|
||||
unit_convert=PRESSURE_INHG,
|
||||
),
|
||||
"seaLevelPressure": NWSSensorMetadata(
|
||||
"Sea Level Pressure",
|
||||
NWSSensorEntityDescription(
|
||||
key="seaLevelPressure",
|
||||
name="Sea Level Pressure",
|
||||
icon=None,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
unit=PRESSURE_PA,
|
||||
unit_of_measurement=PRESSURE_PA,
|
||||
unit_convert=PRESSURE_INHG,
|
||||
),
|
||||
"visibility": NWSSensorMetadata(
|
||||
"Visibility",
|
||||
NWSSensorEntityDescription(
|
||||
key="visibility",
|
||||
name="Visibility",
|
||||
icon="mdi:eye",
|
||||
device_class=None,
|
||||
unit=LENGTH_METERS,
|
||||
unit_of_measurement=LENGTH_METERS,
|
||||
unit_convert=LENGTH_MILES,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
|
|
@ -28,7 +28,7 @@ from .const import (
|
|||
NWS_DATA,
|
||||
OBSERVATION_VALID_TIME,
|
||||
SENSOR_TYPES,
|
||||
NWSSensorMetadata,
|
||||
NWSSensorEntityDescription,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
@ -39,32 +39,29 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||
hass_data = hass.data[DOMAIN][entry.entry_id]
|
||||
station = entry.data[CONF_STATION]
|
||||
|
||||
entities = []
|
||||
for sensor_type, metadata in SENSOR_TYPES.items():
|
||||
entities.append(
|
||||
async_add_entities(
|
||||
NWSSensor(
|
||||
hass,
|
||||
entry.data,
|
||||
hass_data,
|
||||
sensor_type,
|
||||
metadata,
|
||||
station,
|
||||
),
|
||||
hass=hass,
|
||||
entry_data=entry.data,
|
||||
hass_data=hass_data,
|
||||
description=description,
|
||||
station=station,
|
||||
)
|
||||
for description in SENSOR_TYPES
|
||||
)
|
||||
|
||||
async_add_entities(entities, False)
|
||||
|
||||
|
||||
class NWSSensor(CoordinatorEntity, SensorEntity):
|
||||
"""An NWS Sensor Entity."""
|
||||
|
||||
entity_description: NWSSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
entry_data,
|
||||
hass_data,
|
||||
sensor_type,
|
||||
metadata: NWSSensorMetadata,
|
||||
description: NWSSensorEntityDescription,
|
||||
station,
|
||||
):
|
||||
"""Initialise the platform with a data instance."""
|
||||
|
@ -72,32 +69,29 @@ class NWSSensor(CoordinatorEntity, SensorEntity):
|
|||
self._nws = hass_data[NWS_DATA]
|
||||
self._latitude = entry_data[CONF_LATITUDE]
|
||||
self._longitude = entry_data[CONF_LONGITUDE]
|
||||
self._type = sensor_type
|
||||
self._metadata = metadata
|
||||
self.entity_description = description
|
||||
|
||||
self._attr_name = f"{station} {metadata.label}"
|
||||
self._attr_icon = metadata.icon
|
||||
self._attr_device_class = metadata.device_class
|
||||
if hass.config.units.is_metric:
|
||||
self._attr_unit_of_measurement = metadata.unit
|
||||
else:
|
||||
self._attr_unit_of_measurement = metadata.unit_convert
|
||||
self._attr_name = f"{station} {description.name}"
|
||||
if not hass.config.units.is_metric:
|
||||
self._attr_unit_of_measurement = description.unit_convert
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state."""
|
||||
value = self._nws.observation.get(self._type)
|
||||
value = self._nws.observation.get(self.entity_description.key)
|
||||
if value is None:
|
||||
return None
|
||||
if self._attr_unit_of_measurement == SPEED_MILES_PER_HOUR:
|
||||
# Set alias to unit property -> prevent unnecessary hasattr calls
|
||||
unit_of_measurement = self.unit_of_measurement
|
||||
if unit_of_measurement == SPEED_MILES_PER_HOUR:
|
||||
return round(convert_distance(value, LENGTH_KILOMETERS, LENGTH_MILES))
|
||||
if self._attr_unit_of_measurement == LENGTH_MILES:
|
||||
if unit_of_measurement == LENGTH_MILES:
|
||||
return round(convert_distance(value, LENGTH_METERS, LENGTH_MILES))
|
||||
if self._attr_unit_of_measurement == PRESSURE_INHG:
|
||||
if unit_of_measurement == PRESSURE_INHG:
|
||||
return round(convert_pressure(value, PRESSURE_PA, PRESSURE_INHG), 2)
|
||||
if self._attr_unit_of_measurement == TEMP_CELSIUS:
|
||||
if unit_of_measurement == TEMP_CELSIUS:
|
||||
return round(value, 1)
|
||||
if self._attr_unit_of_measurement == PERCENTAGE:
|
||||
if unit_of_measurement == PERCENTAGE:
|
||||
return round(value)
|
||||
return value
|
||||
|
||||
|
@ -109,7 +103,7 @@ class NWSSensor(CoordinatorEntity, SensorEntity):
|
|||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique_id for this entity."""
|
||||
return f"{base_unique_id(self._latitude, self._longitude)}_{self._type}"
|
||||
return f"{base_unique_id(self._latitude, self._longitude)}_{self.entity_description.key}"
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
|
|
|
@ -35,12 +35,12 @@ async def test_imperial_metric(
|
|||
"""Test with imperial and metric units."""
|
||||
registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
for sensor_name, metadata in SENSOR_TYPES.items():
|
||||
for description in SENSOR_TYPES:
|
||||
registry.async_get_or_create(
|
||||
SENSOR_DOMAIN,
|
||||
DOMAIN,
|
||||
f"35_-75_{sensor_name}",
|
||||
suggested_object_id=f"abc_{metadata.label}",
|
||||
f"35_-75_{description.key}",
|
||||
suggested_object_id=f"abc_{description.name}",
|
||||
disabled_by=None,
|
||||
)
|
||||
|
||||
|
@ -53,10 +53,11 @@ async def test_imperial_metric(
|
|||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
for sensor_name, metadata in SENSOR_TYPES.items():
|
||||
state = hass.states.get(f"sensor.abc_{slugify(metadata.label)}")
|
||||
for description in SENSOR_TYPES:
|
||||
assert description.name
|
||||
state = hass.states.get(f"sensor.abc_{slugify(description.name)}")
|
||||
assert state
|
||||
assert state.state == result_observation[sensor_name]
|
||||
assert state.state == result_observation[description.key]
|
||||
assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
|
||||
|
||||
|
||||
|
@ -67,12 +68,12 @@ async def test_none_values(hass, mock_simple_nws, no_weather):
|
|||
|
||||
registry = await hass.helpers.entity_registry.async_get_registry()
|
||||
|
||||
for sensor_name, metadata in SENSOR_TYPES.items():
|
||||
for description in SENSOR_TYPES:
|
||||
registry.async_get_or_create(
|
||||
SENSOR_DOMAIN,
|
||||
DOMAIN,
|
||||
f"35_-75_{sensor_name}",
|
||||
suggested_object_id=f"abc_{metadata.label}",
|
||||
f"35_-75_{description.key}",
|
||||
suggested_object_id=f"abc_{description.name}",
|
||||
disabled_by=None,
|
||||
)
|
||||
|
||||
|
@ -84,7 +85,8 @@ async def test_none_values(hass, mock_simple_nws, no_weather):
|
|||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
for sensor_name, metadata in SENSOR_TYPES.items():
|
||||
state = hass.states.get(f"sensor.abc_{slugify(metadata.label)}")
|
||||
for description in SENSOR_TYPES:
|
||||
assert description.name
|
||||
state = hass.states.get(f"sensor.abc_{slugify(description.name)}")
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
|
Loading…
Reference in New Issue