Improve sensor value parsing and add battery binary sensors.

- Introduce `to_int`/`to_float` helpers to safely handle None/blank payload values.
- Use the converters across weather/wslink sensor descriptions and simplify wind direction handling.
- Add low-battery binary sensor entities and definitions.
ecowitt_support
SchiZzA 2026-03-23 18:21:05 +01:00
parent 9d5fafa8d0
commit 159d465db5
No known key found for this signature in database
6 changed files with 190 additions and 146 deletions

View File

@ -36,11 +36,7 @@ from py_typecheck import checked, checked_or
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ( from homeassistant.exceptions import ConfigEntryNotReady, InvalidStateError, PlatformNotReady
ConfigEntryNotReady,
InvalidStateError,
PlatformNotReady,
)
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import ( from .const import (
@ -215,9 +211,7 @@ class WeatherDataUpdateCoordinator(DataUpdateCoordinator):
raise HTTPUnauthorized raise HTTPUnauthorized
# Convert raw payload keys to our internal sensor keys (stable identifiers). # Convert raw payload keys to our internal sensor keys (stable identifiers).
remaped_items: dict[str, str] = ( remaped_items: dict[str, str] = remap_wslink_items(data) if _wslink else remap_items(data)
remap_wslink_items(data) if _wslink else remap_items(data)
)
# Auto-discovery: if payload contains keys that are not enabled/loaded yet, # Auto-discovery: if payload contains keys that are not enabled/loaded yet,
# add them to the option list and create entities dynamically. # add them to the option list and create entities dynamically.
@ -274,9 +268,7 @@ class WeatherDataUpdateCoordinator(DataUpdateCoordinator):
# NOTE: Some linters prefer top-level imports. In this case the local import is # NOTE: Some linters prefer top-level imports. In this case the local import is
# intentional and prevents "partially initialized module" errors. # intentional and prevents "partially initialized module" errors.
from .sensor import ( # noqa: PLC0415 (local import is intentional) from .sensor import add_new_sensors # noqa: PLC0415 (local import is intentional)
add_new_sensors,
)
add_new_sensors(self.hass, self.config, newly_discovered) add_new_sensors(self.hass, self.config, newly_discovered)
@ -294,9 +286,7 @@ class WeatherDataUpdateCoordinator(DataUpdateCoordinator):
# to avoid additional background polling tasks. # to avoid additional background polling tasks.
_windy_enabled = checked_or(self.config.options.get(WINDY_ENABLED), bool, False) _windy_enabled = checked_or(self.config.options.get(WINDY_ENABLED), bool, False)
_pocasi_enabled = checked_or( _pocasi_enabled = checked_or(self.config.options.get(POCASI_CZ_ENABLED), bool, False)
self.config.options.get(POCASI_CZ_ENABLED), bool, False
)
if _windy_enabled: if _windy_enabled:
await self.windy.push_data_to_windy(data, _wslink) await self.windy.push_data_to_windy(data, _wslink)
@ -342,38 +332,22 @@ def register_path(
# Register webhooks in HomeAssistant with dispatcher # Register webhooks in HomeAssistant with dispatcher
try: try:
_default_route = hass.http.app.router.add_get( _default_route = hass.http.app.router.add_get(DEFAULT_URL, routes.dispatch, name="_default_route")
DEFAULT_URL, routes.dispatch, name="_default_route" _wslink_post_route = hass.http.app.router.add_post(WSLINK_URL, routes.dispatch, name="_wslink_post_route")
) _wslink_get_route = hass.http.app.router.add_get(WSLINK_URL, routes.dispatch, name="_wslink_get_route")
_wslink_post_route = hass.http.app.router.add_post( _health_route = hass.http.app.router.add_get(HEALTH_URL, routes.dispatch, name="_health_route")
WSLINK_URL, routes.dispatch, name="_wslink_post_route"
)
_wslink_get_route = hass.http.app.router.add_get(
WSLINK_URL, routes.dispatch, name="_wslink_get_route"
)
_health_route = hass.http.app.router.add_get(
HEALTH_URL, routes.dispatch, name="_health_route"
)
# Save initialised routes # Save initialised routes
hass_data["routes"] = routes hass_data["routes"] = routes
except RuntimeError as Ex: except RuntimeError as Ex:
_LOGGER.critical( _LOGGER.critical("Routes cannot be added. Integration will not work as expected. %s", Ex)
"Routes cannot be added. Integration will not work as expected. %s", Ex
)
raise ConfigEntryNotReady from Ex raise ConfigEntryNotReady from Ex
# Finally create internal route dispatcher with provided urls, while we have webhooks registered. # Finally create internal route dispatcher with provided urls, while we have webhooks registered.
routes.add_route( routes.add_route(DEFAULT_URL, _default_route, coordinator.received_data, enabled=not _wslink)
DEFAULT_URL, _default_route, coordinator.received_data, enabled=not _wslink routes.add_route(WSLINK_URL, _wslink_post_route, coordinator.received_data, enabled=_wslink)
) routes.add_route(WSLINK_URL, _wslink_get_route, coordinator.received_data, enabled=_wslink)
routes.add_route(
WSLINK_URL, _wslink_post_route, coordinator.received_data, enabled=_wslink
)
routes.add_route(
WSLINK_URL, _wslink_get_route, coordinator.received_data, enabled=_wslink
)
# Make health route `sticky` so it will not change upon updating options. # Make health route `sticky` so it will not change upon updating options.
routes.add_route( routes.add_route(
HEALTH_URL, HEALTH_URL,
@ -449,9 +423,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if routes: if routes:
_LOGGER.debug("We have routes registered, will try to switch dispatcher.") _LOGGER.debug("We have routes registered, will try to switch dispatcher.")
routes.switch_route( routes.switch_route(coordinator.received_data, DEFAULT_URL if not _wslink else WSLINK_URL)
coordinator.received_data, DEFAULT_URL if not _wslink else WSLINK_URL
)
routes.set_ingress_observer(coordinator_health.record_dispatch) routes.set_ingress_observer(coordinator_health.record_dispatch)
coordinator_health.update_routing(routes) coordinator_health.update_routing(routes)
_LOGGER.debug("%s", routes.show_enabled()) _LOGGER.debug("%s", routes.show_enabled())
@ -487,14 +459,8 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry):
""" """
if (hass_data := checked(hass.data.get(DOMAIN), dict[str, Any])) is not None: if (hass_data := checked(hass.data.get(DOMAIN), dict[str, Any])) is not None:
if ( if (entry_data := checked(hass_data.get(entry.entry_id), dict[str, Any])) is not None:
entry_data := checked(hass_data.get(entry.entry_id), dict[str, Any]) if (old_options := checked(entry_data.get(ENTRY_LAST_OPTIONS), dict[str, Any])) is not None:
) is not None:
if (
old_options := checked(
entry_data.get(ENTRY_LAST_OPTIONS), dict[str, Any]
)
) is not None:
new_options = dict(entry.options) new_options = dict(entry.options)
changed_keys = { changed_keys = {
@ -507,9 +473,7 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry):
entry_data[ENTRY_LAST_OPTIONS] = new_options entry_data[ENTRY_LAST_OPTIONS] = new_options
if changed_keys == {SENSORS_TO_LOAD}: if changed_keys == {SENSORS_TO_LOAD}:
_LOGGER.debug( _LOGGER.debug("Options updated (%s); skipping reload.", SENSORS_TO_LOAD)
"Options updated (%s); skipping reload.", SENSORS_TO_LOAD
)
return return
else: else:
# No/invalid snapshot: store current options for next comparison. # No/invalid snapshot: store current options for next comparison.

View File

@ -0,0 +1,53 @@
"""Battery binary sensor entities."""
from __future__ import annotations
from typing import Any
from py_typecheck import checked_or
from homeassistant.components.binary_sensor import BinarySensorEntity, BinarySensorEntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
class BatteryBinarySensor( # pyright: ignore[reportIncompatibleVariableOverride]
CoordinatorEntity, BinarySensorEntity
):
"""Represent a low-battery binary sensor.
Station payload uses:
- ``0`` => low battery (binary sensor is ``on``)
- ``1`` => battery OK (binary sensor is ``off``)
"""
_attr_has_entity_name = True
_attr_should_poll = False
def __init__(
self,
coordinator: Any,
description: BinarySensorEntityDescription,
) -> None:
"""Initialize the battery binary sensor."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = f"{description.key}_battery"
@property
def is_on(self) -> bool | None: # pyright: ignore[reportIncompatibleVariableOverride]
"""Return low-battery state.
``True`` means low battery for ``BinarySensorDeviceClass.BATTERY``.
"""
data = checked_or(self.coordinator.data, dict[str, Any], {})
raw: Any = data.get(self.entity_description.key)
if raw is None or raw == "":
return None
try:
value = int(raw)
except (TypeError, ValueError):
return None
return value == 0

View File

@ -0,0 +1,24 @@
"""Battery sensors."""
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntityDescription,
)
BATTERY_BINARY_SENSORS: tuple[BinarySensorEntityDescription, ...] = (
BinarySensorEntityDescription(
key="outside_battery",
translation_key="outside_battery",
device_class=BinarySensorDeviceClass.BATTERY,
),
BinarySensorEntityDescription(
key="indoor_battery",
translation_key="indoor_battery",
device_class=BinarySensorDeviceClass.BATTERY,
),
BinarySensorEntityDescription(
key="ch2_battery",
translation_key="ch2_battery",
device_class=BinarySensorDeviceClass.BATTERY,
),
)

View File

@ -1,7 +1,5 @@
"""Sensor entities for the SWS12500 integration for old endpoint.""" """Sensor entities for the SWS12500 integration for old endpoint."""
from typing import cast
from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass
from homeassistant.const import ( from homeassistant.const import (
DEGREE, DEGREE,
@ -41,7 +39,7 @@ from .const import (
UnitOfDir, UnitOfDir,
) )
from .sensors_common import WeatherSensorEntityDescription from .sensors_common import WeatherSensorEntityDescription
from .utils import chill_index, heat_index, wind_dir_to_text from .utils import chill_index, heat_index, to_float, to_int, wind_dir_to_text
SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = ( SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
@ -51,7 +49,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
translation_key=INDOOR_TEMP, translation_key=INDOOR_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=INDOOR_HUMIDITY, key=INDOOR_HUMIDITY,
@ -60,7 +58,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
translation_key=INDOOR_HUMIDITY, translation_key=INDOOR_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=OUTSIDE_TEMP, key=OUTSIDE_TEMP,
@ -69,7 +67,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
translation_key=OUTSIDE_TEMP, translation_key=OUTSIDE_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=OUTSIDE_HUMIDITY, key=OUTSIDE_HUMIDITY,
@ -78,7 +76,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
translation_key=OUTSIDE_HUMIDITY, translation_key=OUTSIDE_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=DEW_POINT, key=DEW_POINT,
@ -87,7 +85,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer-lines", icon="mdi:thermometer-lines",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
translation_key=DEW_POINT, translation_key=DEW_POINT,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=BARO_PRESSURE, key=BARO_PRESSURE,
@ -97,7 +95,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
suggested_unit_of_measurement=UnitOfPressure.HPA, suggested_unit_of_measurement=UnitOfPressure.HPA,
translation_key=BARO_PRESSURE, translation_key=BARO_PRESSURE,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_SPEED, key=WIND_SPEED,
@ -107,7 +105,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
icon="mdi:weather-windy", icon="mdi:weather-windy",
translation_key=WIND_SPEED, translation_key=WIND_SPEED,
value_fn=lambda data: cast("int", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_GUST, key=WIND_GUST,
@ -117,7 +115,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
icon="mdi:windsock", icon="mdi:windsock",
translation_key=WIND_GUST, translation_key=WIND_GUST,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_DIR, key=WIND_DIR,
@ -127,15 +125,12 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=None, suggested_display_precision=None,
icon="mdi:sign-direction", icon="mdi:sign-direction",
translation_key=WIND_DIR, translation_key=WIND_DIR,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_AZIMUT, key=WIND_AZIMUT,
icon="mdi:sign-direction", icon="mdi:sign-direction",
value_fn=lambda data: cast("str", wind_dir_to_text(data)), value_from_data_fn=lambda dir: wind_dir_to_text(dir.get(WIND_DIR, 0.0)),
value_from_data_fn=lambda data: cast(
"str", wind_dir_to_text(cast("float", data.get(WIND_DIR) or 0.0))
),
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
options=[e.value for e in UnitOfDir], options=[e.value for e in UnitOfDir],
translation_key=WIND_AZIMUT, translation_key=WIND_AZIMUT,
@ -149,7 +144,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=RAIN, translation_key=RAIN,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=DAILY_RAIN, key=DAILY_RAIN,
@ -160,7 +155,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=DAILY_RAIN, translation_key=DAILY_RAIN,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=SOLAR_RADIATION, key=SOLAR_RADIATION,
@ -169,7 +164,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.IRRADIANCE, device_class=SensorDeviceClass.IRRADIANCE,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=SOLAR_RADIATION, translation_key=SOLAR_RADIATION,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=UV, key=UV,
@ -178,7 +173,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
native_unit_of_measurement=UV_INDEX, native_unit_of_measurement=UV_INDEX,
icon="mdi:sunglasses", icon="mdi:sunglasses",
translation_key=UV, translation_key=UV,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH2_TEMP, key=CH2_TEMP,
@ -188,7 +183,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH2_TEMP, translation_key=CH2_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH2_HUMIDITY, key=CH2_HUMIDITY,
@ -197,7 +192,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH2_HUMIDITY, translation_key=CH2_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH3_TEMP, key=CH3_TEMP,
@ -207,7 +202,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH3_TEMP, translation_key=CH3_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH3_HUMIDITY, key=CH3_HUMIDITY,
@ -216,7 +211,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH3_HUMIDITY, translation_key=CH3_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH4_TEMP, key=CH4_TEMP,
@ -226,7 +221,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH4_TEMP, translation_key=CH4_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH4_HUMIDITY, key=CH4_HUMIDITY,
@ -235,7 +230,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH4_HUMIDITY, translation_key=CH4_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=HEAT_INDEX, key=HEAT_INDEX,
@ -246,7 +241,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=HEAT_INDEX, translation_key=HEAT_INDEX,
value_fn=lambda data: cast("int", data), value_fn=to_int,
value_from_data_fn=heat_index, value_from_data_fn=heat_index,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
@ -258,7 +253,7 @@ SENSOR_TYPES_WEATHER_API: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CHILL_INDEX, translation_key=CHILL_INDEX,
value_fn=lambda data: cast("int", data), value_fn=to_int,
value_from_data_fn=chill_index, value_from_data_fn=chill_index,
), ),
) )

View File

@ -1,7 +1,5 @@
"""Sensor entities for the SWS12500 integration for old endpoint.""" """Sensor entities for the SWS12500 integration for old endpoint."""
from typing import cast
from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass from homeassistant.components.sensor import SensorDeviceClass, SensorStateClass
from homeassistant.const import ( from homeassistant.const import (
DEGREE, DEGREE,
@ -64,7 +62,7 @@ from .const import (
UnitOfDir, UnitOfDir,
) )
from .sensors_common import WeatherSensorEntityDescription from .sensors_common import WeatherSensorEntityDescription
from .utils import battery_level, wind_dir_to_text from .utils import battery_level, to_float, to_int, wind_dir_to_text
SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = ( SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
@ -74,7 +72,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
translation_key=INDOOR_TEMP, translation_key=INDOOR_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=INDOOR_HUMIDITY, key=INDOOR_HUMIDITY,
@ -83,7 +81,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
translation_key=INDOOR_HUMIDITY, translation_key=INDOOR_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=OUTSIDE_TEMP, key=OUTSIDE_TEMP,
@ -92,7 +90,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
translation_key=OUTSIDE_TEMP, translation_key=OUTSIDE_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=OUTSIDE_HUMIDITY, key=OUTSIDE_HUMIDITY,
@ -101,7 +99,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer", icon="mdi:thermometer",
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
translation_key=OUTSIDE_HUMIDITY, translation_key=OUTSIDE_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=DEW_POINT, key=DEW_POINT,
@ -110,7 +108,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
icon="mdi:thermometer-lines", icon="mdi:thermometer-lines",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
translation_key=DEW_POINT, translation_key=DEW_POINT,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=BARO_PRESSURE, key=BARO_PRESSURE,
@ -120,7 +118,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE, device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
suggested_unit_of_measurement=UnitOfPressure.HPA, suggested_unit_of_measurement=UnitOfPressure.HPA,
translation_key=BARO_PRESSURE, translation_key=BARO_PRESSURE,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_SPEED, key=WIND_SPEED,
@ -130,7 +128,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
icon="mdi:weather-windy", icon="mdi:weather-windy",
translation_key=WIND_SPEED, translation_key=WIND_SPEED,
value_fn=lambda data: cast("int", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_GUST, key=WIND_GUST,
@ -140,7 +138,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR, suggested_unit_of_measurement=UnitOfSpeed.KILOMETERS_PER_HOUR,
icon="mdi:windsock", icon="mdi:windsock",
translation_key=WIND_GUST, translation_key=WIND_GUST,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_DIR, key=WIND_DIR,
@ -150,15 +148,12 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=None, suggested_display_precision=None,
icon="mdi:sign-direction", icon="mdi:sign-direction",
translation_key=WIND_DIR, translation_key=WIND_DIR,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WIND_AZIMUT, key=WIND_AZIMUT,
icon="mdi:sign-direction", icon="mdi:sign-direction",
value_fn=lambda data: cast("str", wind_dir_to_text(data)), value_from_data_fn=lambda dir: wind_dir_to_text(dir.get(WIND_DIR, 0.0)),
value_from_data_fn=lambda data: cast(
"str", wind_dir_to_text(cast("float", data.get(WIND_DIR) or 0.0))
),
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
options=[e.value for e in UnitOfDir], options=[e.value for e in UnitOfDir],
translation_key=WIND_AZIMUT, translation_key=WIND_AZIMUT,
@ -172,7 +167,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=RAIN, translation_key=RAIN,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=DAILY_RAIN, key=DAILY_RAIN,
@ -183,7 +178,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=DAILY_RAIN, translation_key=DAILY_RAIN,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=HOURLY_RAIN, key=HOURLY_RAIN,
@ -194,7 +189,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=HOURLY_RAIN, translation_key=HOURLY_RAIN,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WEEKLY_RAIN, key=WEEKLY_RAIN,
@ -205,7 +200,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=WEEKLY_RAIN, translation_key=WEEKLY_RAIN,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=MONTHLY_RAIN, key=MONTHLY_RAIN,
@ -216,7 +211,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=MONTHLY_RAIN, translation_key=MONTHLY_RAIN,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=YEARLY_RAIN, key=YEARLY_RAIN,
@ -227,7 +222,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-pouring", icon="mdi:weather-pouring",
translation_key=YEARLY_RAIN, translation_key=YEARLY_RAIN,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=SOLAR_RADIATION, key=SOLAR_RADIATION,
@ -236,7 +231,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.IRRADIANCE, device_class=SensorDeviceClass.IRRADIANCE,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=SOLAR_RADIATION, translation_key=SOLAR_RADIATION,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=UV, key=UV,
@ -245,7 +240,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
native_unit_of_measurement=UV_INDEX, native_unit_of_measurement=UV_INDEX,
icon="mdi:sunglasses", icon="mdi:sunglasses",
translation_key=UV, translation_key=UV,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH2_TEMP, key=CH2_TEMP,
@ -255,7 +250,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH2_TEMP, translation_key=CH2_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH2_HUMIDITY, key=CH2_HUMIDITY,
@ -264,7 +259,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH2_HUMIDITY, translation_key=CH2_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH3_TEMP, key=CH3_TEMP,
@ -274,7 +269,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH3_TEMP, translation_key=CH3_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH3_HUMIDITY, key=CH3_HUMIDITY,
@ -283,7 +278,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH3_HUMIDITY, translation_key=CH3_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH4_TEMP, key=CH4_TEMP,
@ -293,7 +288,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH4_TEMP, translation_key=CH4_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH4_HUMIDITY, key=CH4_HUMIDITY,
@ -302,7 +297,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH4_HUMIDITY, translation_key=CH4_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH5_TEMP, key=CH5_TEMP,
@ -312,7 +307,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH5_TEMP, translation_key=CH5_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH5_HUMIDITY, key=CH5_HUMIDITY,
@ -321,7 +316,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH5_HUMIDITY, translation_key=CH5_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH6_TEMP, key=CH6_TEMP,
@ -331,7 +326,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH6_TEMP, translation_key=CH6_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH6_HUMIDITY, key=CH6_HUMIDITY,
@ -340,7 +335,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH6_HUMIDITY, translation_key=CH6_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH7_TEMP, key=CH7_TEMP,
@ -350,7 +345,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH7_TEMP, translation_key=CH7_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH7_HUMIDITY, key=CH7_HUMIDITY,
@ -359,7 +354,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH7_HUMIDITY, translation_key=CH7_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH8_TEMP, key=CH8_TEMP,
@ -369,7 +364,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_unit_of_measurement=UnitOfTemperature.CELSIUS, suggested_unit_of_measurement=UnitOfTemperature.CELSIUS,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH8_TEMP, translation_key=CH8_TEMP,
value_fn=lambda data: cast("float", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH8_HUMIDITY, key=CH8_HUMIDITY,
@ -378,34 +373,28 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.HUMIDITY, device_class=SensorDeviceClass.HUMIDITY,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CH8_HUMIDITY, translation_key=CH8_HUMIDITY,
value_fn=lambda data: cast("int", data), value_fn=to_int,
),
WeatherSensorEntityDescription(
key=HEAT_INDEX,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.ENUM,
value_fn=lambda data: data,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH3_BATTERY, key=CH3_BATTERY,
translation_key=CH3_BATTERY, translation_key=CH3_BATTERY,
icon="mdi:battery-unknown", icon="mdi:battery-unknown",
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
value_fn=lambda data: data, value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH4_BATTERY, key=CH4_BATTERY,
translation_key=CH4_BATTERY, translation_key=CH4_BATTERY,
icon="mdi:battery-unknown", icon="mdi:battery-unknown",
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
value_fn=lambda data: data, value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH5_BATTERY, key=CH5_BATTERY,
translation_key=CH5_BATTERY, translation_key=CH5_BATTERY,
icon="mdi:battery-unknown", icon="mdi:battery-unknown",
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
value_fn=lambda data: data, value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH6_BATTERY, key=CH6_BATTERY,
@ -419,14 +408,14 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
translation_key=CH7_BATTERY, translation_key=CH7_BATTERY,
icon="mdi:battery-unknown", icon="mdi:battery-unknown",
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
value_fn=lambda data: data, value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CH8_BATTERY, key=CH8_BATTERY,
translation_key=CH8_BATTERY, translation_key=CH8_BATTERY,
icon="mdi:battery-unknown", icon="mdi:battery-unknown",
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
value_fn=lambda data: data, value_fn=to_int,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=HEAT_INDEX, key=HEAT_INDEX,
@ -437,7 +426,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=HEAT_INDEX, translation_key=HEAT_INDEX,
value_fn=lambda data: cast("int", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=CHILL_INDEX, key=CHILL_INDEX,
@ -448,7 +437,7 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
suggested_display_precision=2, suggested_display_precision=2,
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
translation_key=CHILL_INDEX, translation_key=CHILL_INDEX,
value_fn=lambda data: cast("int", data), value_fn=to_float,
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=OUTSIDE_BATTERY, key=OUTSIDE_BATTERY,
@ -473,12 +462,8 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=INDOOR_BATTERY, key=INDOOR_BATTERY,
translation_key=INDOOR_BATTERY, translation_key=INDOOR_BATTERY,
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.BATTERY,
options=[e.value for e in UnitOfBat], value_fn=to_int,
value_fn=None,
value_from_data_fn=lambda data: (
battery_level(data.get(INDOOR_BATTERY, None)).value
),
), ),
WeatherSensorEntityDescription( WeatherSensorEntityDescription(
key=WBGT_TEMP, key=WBGT_TEMP,
@ -488,6 +473,6 @@ SENSOR_TYPES_WSLINK: tuple[WeatherSensorEntityDescription, ...] = (
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_unit_of_measurement=UnitOfTemperature.CELSIUS,
suggested_display_precision=2, suggested_display_precision=2,
value_fn=lambda data: cast("int", data), value_fn=to_float,
), ),
) )

View File

@ -204,8 +204,10 @@ def wind_dir_to_text(deg: float) -> UnitOfDir | None:
Returns UnitOfDir or None Returns UnitOfDir or None
""" """
if deg: _deg = to_float(deg)
return AZIMUT[int(abs((float(deg) - 11.25) % 360) / 22.5)] if _deg is not None:
_LOGGER.debug("wind_dir: %s", AZIMUT[int(abs((_deg - 11.25) % 360) / 22.5)])
return AZIMUT[int(abs((_deg - 11.25) % 360) / 22.5)]
return None return None
@ -263,11 +265,32 @@ def celsius_to_fahrenheit(celsius: float) -> float:
return celsius * 9.0 / 5.0 + 32 return celsius * 9.0 / 5.0 + 32
def _to_float(val: Any) -> float | None: def to_int(val: Any) -> int | None:
"""Convert int or string to int."""
if val is None:
return None
if isinstance(val, str) and val.strip() == "":
return None
try:
v = int(val)
except (TypeError, ValueError):
return None
else:
return v
def to_float(val: Any) -> float | None:
"""Convert int or string to float.""" """Convert int or string to float."""
if not val: if val is None:
return None return None
if isinstance(val, str) and val.strip() == "":
return None
try: try:
v = float(val) v = float(val)
except (TypeError, ValueError): except (TypeError, ValueError):
@ -284,14 +307,14 @@ def heat_index(
data: dict with temperature and humidity data: dict with temperature and humidity
convert: bool, convert recieved data from Celsius to Fahrenheit convert: bool, convert recieved data from Celsius to Fahrenheit
""" """
if (temp := _to_float(data.get(OUTSIDE_TEMP))) is None: if (temp := to_float(data.get(OUTSIDE_TEMP))) is None:
_LOGGER.error( _LOGGER.error(
"We are missing/invalid OUTSIDE TEMP (%s), cannot calculate wind chill index.", "We are missing/invalid OUTSIDE TEMP (%s), cannot calculate wind chill index.",
temp, temp,
) )
return None return None
if (rh := _to_float(data.get(OUTSIDE_HUMIDITY))) is None: if (rh := to_float(data.get(OUTSIDE_HUMIDITY))) is None:
_LOGGER.error( _LOGGER.error(
"We are missing/invalid OUTSIDE HUMIDITY (%s), cannot calculate wind chill index.", "We are missing/invalid OUTSIDE HUMIDITY (%s), cannot calculate wind chill index.",
rh, rh,
@ -335,8 +358,8 @@ def chill_index(
data: dict with temperature and wind speed data: dict with temperature and wind speed
convert: bool, convert recieved data from Celsius to Fahrenheit convert: bool, convert recieved data from Celsius to Fahrenheit
""" """
temp = _to_float(data.get(OUTSIDE_TEMP)) temp = to_float(data.get(OUTSIDE_TEMP))
wind = _to_float(data.get(WIND_SPEED)) wind = to_float(data.get(WIND_SPEED))
if temp is None: if temp is None:
_LOGGER.error( _LOGGER.error(