1 """Sensor component that handles additional Tomorrowio data for your location."""
3 from __future__
import annotations
5 from abc
import abstractmethod
6 from collections.abc
import Callable
7 from dataclasses
import dataclass
10 from pytomorrowio.const
import (
21 SensorEntityDescription,
26 CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
27 CONCENTRATION_PARTS_PER_MILLION,
43 TMRW_ATTR_CARBON_MONOXIDE,
45 TMRW_ATTR_CHINA_HEALTH_CONCERN,
46 TMRW_ATTR_CHINA_PRIMARY_POLLUTANT,
48 TMRW_ATTR_CLOUD_CEILING,
49 TMRW_ATTR_CLOUD_COVER,
52 TMRW_ATTR_EPA_HEALTH_CONCERN,
53 TMRW_ATTR_EPA_PRIMARY_POLLUTANT,
56 TMRW_ATTR_NITROGEN_DIOXIDE,
58 TMRW_ATTR_PARTICULATE_MATTER_10,
59 TMRW_ATTR_PARTICULATE_MATTER_25,
60 TMRW_ATTR_POLLEN_GRASS,
61 TMRW_ATTR_POLLEN_TREE,
62 TMRW_ATTR_POLLEN_WEED,
63 TMRW_ATTR_PRECIPITATION_TYPE,
64 TMRW_ATTR_PRESSURE_SURFACE_LEVEL,
66 TMRW_ATTR_SULPHUR_DIOXIDE,
67 TMRW_ATTR_UV_HEALTH_CONCERN,
71 from .coordinator
import TomorrowioDataUpdateCoordinator
72 from .entity
import TomorrowioEntity
75 @dataclass(frozen=True)
77 """Describes a Tomorrow.io sensor entity."""
80 unit_imperial: str |
None =
None
81 unit_metric: str |
None =
None
82 multiplication_factor: Callable[[float], float] | float |
None =
None
83 imperial_conversion: Callable[[float], float] | float |
None =
None
84 value_map: Any |
None =
None
87 """Handle post init."""
88 if (self.unit_imperial
is None and self.unit_metric
is not None)
or (
89 self.unit_imperial
is not None and self.unit_metric
is None
92 "Entity descriptions must include both imperial and metric units or "
93 "they must both be None"
96 if self.value_map
is not None:
97 options = [item.name.lower()
for item
in self.value_map]
98 object.__setattr__(self,
"device_class", SensorDeviceClass.ENUM)
99 object.__setattr__(self,
"options", options)
105 """Return function to convert ppb to ug/m^3."""
106 return lambda x: (x * molecular_weight) / 24.45
112 translation_key=
"feels_like",
113 attribute=TMRW_ATTR_FEELS_LIKE,
114 native_unit_of_measurement=UnitOfTemperature.CELSIUS,
115 device_class=SensorDeviceClass.TEMPERATURE,
116 state_class=SensorStateClass.MEASUREMENT,
120 translation_key=
"dew_point",
121 attribute=TMRW_ATTR_DEW_POINT,
122 native_unit_of_measurement=UnitOfTemperature.CELSIUS,
123 device_class=SensorDeviceClass.TEMPERATURE,
124 state_class=SensorStateClass.MEASUREMENT,
128 key=
"pressure_surface_level",
129 attribute=TMRW_ATTR_PRESSURE_SURFACE_LEVEL,
130 native_unit_of_measurement=UnitOfPressure.HPA,
131 device_class=SensorDeviceClass.PRESSURE,
132 state_class=SensorStateClass.MEASUREMENT,
137 key=
"global_horizontal_irradiance",
138 attribute=TMRW_ATTR_SOLAR_GHI,
139 unit_imperial=UnitOfIrradiance.BTUS_PER_HOUR_SQUARE_FOOT,
140 unit_metric=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
141 imperial_conversion=(1 / 3.15459),
142 device_class=SensorDeviceClass.IRRADIANCE,
143 state_class=SensorStateClass.MEASUREMENT,
148 translation_key=
"cloud_base",
149 attribute=TMRW_ATTR_CLOUD_BASE,
150 unit_imperial=UnitOfLength.MILES,
151 unit_metric=UnitOfLength.KILOMETERS,
152 device_class=SensorDeviceClass.DISTANCE,
153 state_class=SensorStateClass.MEASUREMENT,
154 imperial_conversion=
lambda val: DistanceConverter.convert(
156 UnitOfLength.KILOMETERS,
163 translation_key=
"cloud_ceiling",
164 attribute=TMRW_ATTR_CLOUD_CEILING,
165 unit_imperial=UnitOfLength.MILES,
166 unit_metric=UnitOfLength.KILOMETERS,
167 device_class=SensorDeviceClass.DISTANCE,
168 state_class=SensorStateClass.MEASUREMENT,
169 imperial_conversion=
lambda val: DistanceConverter.convert(
171 UnitOfLength.KILOMETERS,
177 translation_key=
"cloud_cover",
178 attribute=TMRW_ATTR_CLOUD_COVER,
179 native_unit_of_measurement=PERCENTAGE,
184 translation_key=
"wind_gust",
185 attribute=TMRW_ATTR_WIND_GUST,
186 unit_imperial=UnitOfSpeed.MILES_PER_HOUR,
187 unit_metric=UnitOfSpeed.METERS_PER_SECOND,
188 device_class=SensorDeviceClass.SPEED,
189 state_class=SensorStateClass.MEASUREMENT,
190 imperial_conversion=
lambda val: SpeedConverter.convert(
191 val, UnitOfSpeed.METERS_PER_SECOND, UnitOfSpeed.MILES_PER_HOUR
195 key=
"precipitation_type",
196 translation_key=
"precipitation_type",
197 attribute=TMRW_ATTR_PRECIPITATION_TYPE,
198 value_map=PrecipitationType,
204 attribute=TMRW_ATTR_OZONE,
205 native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
207 device_class=SensorDeviceClass.OZONE,
208 state_class=SensorStateClass.MEASUREMENT,
211 key=
"particulate_matter_2_5_mm",
212 attribute=TMRW_ATTR_PARTICULATE_MATTER_25,
213 native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
214 device_class=SensorDeviceClass.PM25,
215 state_class=SensorStateClass.MEASUREMENT,
218 key=
"particulate_matter_10_mm",
219 attribute=TMRW_ATTR_PARTICULATE_MATTER_10,
220 native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
221 device_class=SensorDeviceClass.PM10,
222 state_class=SensorStateClass.MEASUREMENT,
227 key=
"nitrogen_dioxide",
228 attribute=TMRW_ATTR_NITROGEN_DIOXIDE,
229 native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
231 device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
232 state_class=SensorStateClass.MEASUREMENT,
236 key=
"carbon_monoxide",
237 attribute=TMRW_ATTR_CARBON_MONOXIDE,
238 native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
239 multiplication_factor=1 / 1000,
240 device_class=SensorDeviceClass.CO,
241 state_class=SensorStateClass.MEASUREMENT,
246 key=
"sulphur_dioxide",
247 attribute=TMRW_ATTR_SULPHUR_DIOXIDE,
248 native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
250 device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
251 state_class=SensorStateClass.MEASUREMENT,
254 key=
"us_epa_air_quality_index",
255 translation_key=
"us_epa_air_quality_index",
256 attribute=TMRW_ATTR_EPA_AQI,
257 device_class=SensorDeviceClass.AQI,
258 state_class=SensorStateClass.MEASUREMENT,
261 key=
"us_epa_primary_pollutant",
262 translation_key=
"primary_pollutant",
263 attribute=TMRW_ATTR_EPA_PRIMARY_POLLUTANT,
264 value_map=PrimaryPollutantType,
267 key=
"us_epa_health_concern",
268 translation_key=
"health_concern",
269 attribute=TMRW_ATTR_EPA_HEALTH_CONCERN,
270 value_map=HealthConcernType,
273 key=
"china_mep_air_quality_index",
274 translation_key=
"china_mep_air_quality_index",
275 attribute=TMRW_ATTR_CHINA_AQI,
276 device_class=SensorDeviceClass.AQI,
279 key=
"china_mep_primary_pollutant",
280 translation_key=
"china_mep_primary_pollutant",
281 attribute=TMRW_ATTR_CHINA_PRIMARY_POLLUTANT,
282 value_map=PrimaryPollutantType,
285 key=
"china_mep_health_concern",
286 translation_key=
"china_mep_health_concern",
287 attribute=TMRW_ATTR_CHINA_HEALTH_CONCERN,
288 value_map=HealthConcernType,
291 key=
"tree_pollen_index",
292 translation_key=
"pollen_index",
293 attribute=TMRW_ATTR_POLLEN_TREE,
294 value_map=PollenIndex,
297 key=
"weed_pollen_index",
298 translation_key=
"weed_pollen_index",
299 attribute=TMRW_ATTR_POLLEN_WEED,
300 value_map=PollenIndex,
303 key=
"grass_pollen_index",
304 translation_key=
"grass_pollen_index",
305 attribute=TMRW_ATTR_POLLEN_GRASS,
306 value_map=PollenIndex,
310 translation_key=
"fire_index",
311 attribute=TMRW_ATTR_FIRE_INDEX,
315 translation_key=
"uv_index",
316 attribute=TMRW_ATTR_UV_INDEX,
317 state_class=SensorStateClass.MEASUREMENT,
320 key=
"uv_radiation_health_concern",
321 translation_key=
"uv_radiation_health_concern",
322 attribute=TMRW_ATTR_UV_HEALTH_CONCERN,
323 value_map=UVDescription,
330 config_entry: ConfigEntry,
331 async_add_entities: AddEntitiesCallback,
333 """Set up a config entry."""
334 coordinator = hass.data[DOMAIN][config_entry.data[CONF_API_KEY]]
337 for description
in SENSOR_TYPES
343 value: float, conversion: Callable[[float], float] | float
345 """Handle conversion of a value based on conversion type."""
346 if callable(conversion):
347 return round(conversion(
float(value)), 2)
349 return round(
float(value) * conversion, 2)
353 """Base Tomorrow.io sensor entity."""
355 entity_description: TomorrowioSensorEntityDescription
356 _attr_entity_registry_enabled_default =
False
361 config_entry: ConfigEntry,
362 coordinator: TomorrowioDataUpdateCoordinator,
364 description: TomorrowioSensorEntityDescription,
366 """Initialize Tomorrow.io Sensor Entity."""
367 super().
__init__(config_entry, coordinator, api_version)
369 self.
_attr_unique_id_attr_unique_id = f
"{self._config_entry.unique_id}_{description.key}"
372 if hass.config.units
is US_CUSTOMARY_SYSTEM:
378 """Return the raw state."""
382 """Return the state."""
389 if desc.value_map
is not None:
390 return desc.value_map(state).name.lower()
392 if desc.multiplication_factor
is not None:
398 desc.imperial_conversion
399 and desc.unit_imperial
is not None
400 and desc.unit_imperial != desc.unit_metric
401 and self.
hasshasshass.config.units
is US_CUSTOMARY_SYSTEM
409 """Sensor entity that talks to Tomorrow.io v4 API to retrieve non-weather data."""
413 """Return the raw state."""
415 assert not isinstance(val, str)
int|str|float|None _get_current_property(self, str property_name)
str|int|float|None native_value(self)
None __init__(self, HomeAssistant hass, ConfigEntry config_entry, TomorrowioDataUpdateCoordinator coordinator, int api_version, TomorrowioSensorEntityDescription description)
_attr_native_unit_of_measurement
int|float|None _state(self)
int|float|None _state(self)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Callable[[float], float] convert_ppb_to_ugm3(float molecular_weight)
float handle_conversion(float value, Callable[[float], float]|float conversion)