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